gpt4 book ai didi

c# - 异步/等待 : a correct way to make background task non-blocking

转载 作者:太空宇宙 更新时间:2023-11-03 19:44:54 25 4
gpt4 key购买 nike

EDIT: from OP's comment, the goal is non-blocking background task so that the rest remain responsive

假设我有这样一个函数:

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

这是一个好方法吗?

感谢您的任何见解,因为我在异步处理方面的实践经验很少。

最佳答案

EDITED 包括评论中的所有建议并添加了一些背景。

背景与见解

将一个函数转换为async 不足以使整个过程成为非阻塞的。为了实现你想要的,整个处理路径(调用堆栈)应该转换为非阻塞。调用堆栈上的一种阻塞方法足以使整个进程阻塞。 非阻塞 并不一定意味着异步,如以下一些示例所示。

将一个方法转换成async有两个步骤:

  • 更改签名以返回任务。这将允许您的调用者跟踪您的方法的状态和结果。
  • async 关键字添加到签名中。这将允许在您的方法主体中使用 await-ing 其他 async 方法。

CPU 绑定(bind)任务与 IO 绑定(bind)任务

请注意,即使您await 一个方法,也不一定意味着您立即将线程释放回您的调用者。被await-ed 的方法只会在它依次开始await-ing 以进行IO 绑定(bind)操作时才将线程释放回给您。当 await-ed 方法在第一次 await-ing 进行 IO 绑定(bind)操作之前执行 CPU 绑定(bind)操作时,您的线程仍将阻塞。

例如:

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

如果因为必须实现这样的接口(interface)而无法更改签名:

public interface ISomeInterface
{
void OnFrameSampleAcquired(VideoCaptureSample sample);
// ... other members
}

然后您可以将async 关键字添加到您的方法中,并且仍然符合接口(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

此选项的缺点是调用者无法跟踪任务的状态(仍在运行或已完成?),也无法跟踪其结果(已完成或抛出异常?)。您可能必须将 OnFrameSampleAcquired 的整个主体包装在 try/catch 中,并将异常写入日志。

选项 4

从技术上讲,您还可以在 Task 上使用 Wait 从非异步 OnFrameSampleAcquired 调用异步 ProcessAsynchronously,但它不会实现您的目标有一个非阻塞后台任务Wait() 将阻塞线程,直到异步处理完成:

void OnFrameSampleAcquired(VideoCaptureSample sample)
{
//Some code here

//Here I want to introduce an Asynchrnous process
ProcessAsynchronously(_latestImageBytes).Wait();

//some more code here
}

关于c# - 异步/等待 : a correct way to make background task non-blocking,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47220217/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com