[英]Async/await: a correct way to make background task non-blocking
编辑:从OP的评论,目标是非阻塞后台任务,以便其余保持响应
说我有一个这样的功能:
void OnFrameSampleAcquired(VideoCaptureSample sample)
{
//Some code here
//Here I want to introduce an Asynchrnous process
ProcessAsynchronously(_latestImageBytes);
//some more code here
}
在评论中,我想介绍一个对异步函数的调用。 现在,我无法修改OnFrameSampleAcquired
(这意味着我无法使其“异步”)。 我怎样才能做到这一点?
我在想
async void ProcessAsynchronously(byte[] image)
{
await Process1(image);
await Process2(image);
// ...
}
或async Task ProcessAsynchronously(byte[] image)
其中ProcessX也被声明为async
这是一个好方法吗?
感谢您提供任何见解,因为我在异步处理方面的实践经验很少。
编辑包括评论中的所有建议,并增加了一些背景。
将一个函数转换为async
不足以使整个过程保持非阻塞状态。 为了实现所需的功能,应将整个处理路径(调用堆栈)转换为非阻塞。 调用堆栈上的一种阻塞方法足以呈现整个进程阻塞。 如下面的一些示例所示, 非阻塞并不一定意味着async
。
将方法转换为async
涉及两个步骤:
Task
。 这将使您的呼叫者可以跟踪方法的状态和结果。 async
关键字添加到签名。 这将允许在您的方法主体中await
其他async
方法。 请注意,即使在await
方法时,也不一定意味着您立即将线程释放回调用方。 await
的方法仅在依次开始await
IO绑定操作时才将线程释放给您。 当await
-ed方法在首次await
-IO绑定操作之前执行CPU绑定操作时,您的线程仍将阻塞。
例如:
async Task MyAsyncMethod()
{
Thread.Sleep(5000); // equivalent to CPU-bound operations
}
await MyAsyncMethod(); // will block for 5 seconds
另一方面,
async Task MyAsyncMethod()
{
await Task.Delay(5000); // equivalent to IO-bound operations
}
await MyAsyncMethod(); // will return immediately
如果您有CPU限制的任务,但仍然不想阻止调用者线程,则可以采用以下解决方法:
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()
由于我不确定为什么不能将OnFrameSampleAcquired签名更改为async
,因此我将建议几个不同的选项。
选项1
最简单,真正异步的方法是:
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);
// ...
}
如果处理路径上的所有方法看起来都像这样,则说明您已正确实现了非阻塞后台作业。
选项2
如果您绝对无法更改签名OnFrameSampleAcquired,则有一种解决方法。 相反,您可以按照@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
}
在这里,您会赢得双赢:首先,您不必更改OnFrameSampleAcquired的签名; 其次,OnFrameSampleAcquired现在是一种非阻塞方法。
选项3
如果由于必须实现以下接口而无法更改签名:
public interface ISomeInterface
{
void OnFrameSampleAcquired(VideoCaptureSample sample);
// ... other members
}
那么您可以将async
关键字添加到您的方法中,并且仍然符合该接口:
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
此选项的缺点是,调用者无法跟踪任务的状态(仍在运行还是已完成?),也无法跟踪其结果(已完成还是抛出异常?)。 您可能必须在try/catch
包装OnFrameSampleAcquired的整个主体,并编写一个异常以进行日志记录。
选项4
从技术上讲,您还可以使用“ Wait
Task
从非异步OnFrameSampleAcquired异步调用异步ProcessAs,但这不会实现拥有非阻塞后台任务的目标 。 Wait()
将阻塞线程,直到异步处理完成:
void OnFrameSampleAcquired(VideoCaptureSample sample)
{
//Some code here
//Here I want to introduce an Asynchrnous process
ProcessAsynchronously(_latestImageBytes).Wait();
//some more code here
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.