EDIT: from OP's comment, the goal is non-blocking background task so that the rest remain responsive
Say I have a function like this:
void OnFrameSampleAcquired(VideoCaptureSample sample)
{
//Some code here
//Here I want to introduce an Asynchrnous process
ProcessAsynchronously(_latestImageBytes);
//some more code here
}
In the point commented, I want to introduce a call to an asynchronous function. Now, I can not modify OnFrameSampleAcquired
(meaning I can not make it "async"). How can I do this?
I am thinking
async void ProcessAsynchronously(byte[] image)
{
await Process1(image);
await Process2(image);
// ...
}
or async Task ProcessAsynchronously(byte[] image)
where ProcessX are also declared as async
Is this a good approach?
Thanks for any insight, since my practical experience with asynchronous processing is very few.
EDITED included all suggestions from the comments and added some background.
Converting one function to async
won't be enough to make the whole process non-blocking. To achieve what you want, the whole processing path (call stack) should be converted to non-blocking. One blocking method on your call stack is enough to render the whole process blocking. Non-blocking does not necessarily mean async
, as some of the examples below will show.
There are two steps involved in converting a method into async
:
Task
. This will allow your callers track status and outcome of your method. async
keyword to the signature. This will allow await
-ing other async
methods in the body of your method. Note that even when you await
for a method, it doesn't necessarily mean that you immediately release the thread back to your caller. The method being await
-ed will release the thread back to you only as soon as it in turn begins await
-ing for an IO-bound operation. Your thread will still block while the await
-ed method performs CPU-bound operations before the first time it is await
-ing for an IO-bound operation.
For example:
async Task MyAsyncMethod()
{
Thread.Sleep(5000); // equivalent to CPU-bound operations
}
await MyAsyncMethod(); // will block for 5 seconds
On the other hand,
async Task MyAsyncMethod()
{
await Task.Delay(5000); // equivalent to IO-bound operations
}
await MyAsyncMethod(); // will return immediately
And the workaround if you have CPU-bound tasks but still don't want to block the caller thread:
async Task MyAsyncMethod()
{
await Task.Yield(); // this does the magic
Thread.Sleep(5000); // equivalent to CPU-bound operations
}
await MyAsyncMethod(); // will return immediately thanks to Task.Yield()
Since I'm not sure why you cannot change OnFrameSampleAcquired signature to async
, I will suggest several different options.
Option 1
The simplest and the truly asynchronous approach would be this:
async Task OnFrameSampleAcquired(VideoCaptureSample sample)
{
//Some code here
//Here I want to introduce an Asynchrnous process
await ProcessAsynchronously(_latestImageBytes);
//some more code here -- provided it is either async or non-blocking!
}
async Task ProcessAsynchronously(byte[] image)
{
await Process1(image);
await Process2(image);
// ...
}
If all of the methods on your processing path look like these, you have a properly implemented non-blocking background job.
Option 2
If you're absolutely unable to change the signature OnFrameSampleAcquired, there is a workaround. You can instead invoke the rest of the processing asynchronously, as suggested by @Fildor:
public void OnFrameSampleAcquired(VideoCaptureSample sample)
{
//Some code here
//Here I want to introduce an Asynchrnous process
ProcessAsynchronously(_latestImageBytes).ContinueWith(task => {
// this runs on a different thread after ProcessAsynchronously is completed
// some more code here
});
// return without blocking
}
Here you win on both sides: first, you don't have to change the signature of OnFrameSampleAcquired; second, OnFrameSampleAcquired is now a non-blocking method.
Option 3
If you cannot change your signature because you must implement an interface like this:
public interface ISomeInterface
{
void OnFrameSampleAcquired(VideoCaptureSample sample);
// ... other members
}
then you can add the async
keyword to your method and still comply with the interface:
async void OnFrameSampleAcquired(VideoCaptureSample sample)
{
//Some code here
//Here I want to introduce an Asynchrnous process
await ProcessAsynchronously(_latestImageBytes);
//some more code here
}
ISomeInterface x; // initialized elsewhere
x.OnFrameSampleAcquired(/*....*/); // the same as await, but no error handling
The drawback of this option is that the callers cannot track nor the status of the task (still running or completed?), neither its outcome (completed or threw exception?). You will probably have to wrap the entire body of OnFrameSampleAcquired in a try/catch
, and write an exception to log.
Option 4
Technically, you can also invoke an async ProcessAsynchronously from a non-async OnFrameSampleAcquired using Wait
on a Task
, but it won't achieve your goal of having a non-blocking background task . The Wait()
will block the thread until the async processing is done:
void OnFrameSampleAcquired(VideoCaptureSample sample)
{
//Some code here
//Here I want to introduce an Asynchrnous process
ProcessAsynchronously(_latestImageBytes).Wait();
//some more code here
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.