来自https://msdn.microsoft.com/en-us/library/mt674882.aspx#Threads:
基于异步的异步编程方法比较好 几乎在每种情况下现有的方法。特别是这种方法 对于IO绑定操作,它比BackgroundWorker更好,因为 代码更简单,您不必防范竞争条件。 与Task.Run结合使用,异步编程优于 BackgroundWorker用于CPU绑定操作,因为异步编程 将运行代码的协调细节与工作分开 Task.Run转移到线程池。
在我看来,async
函数链最终必须等待在程序控制范围之外发生的事情。一些互联网下载或用户输入或其他东西。
当您的程序必须执行一些冗长的计算时,情况怎么样?它必须是一个本身不使用await
的方法,因为在它自己完成所有工作时没有什么可以等待的。如果没有使用await
那么控制不会返回到调用函数,对吗?如果是这样的话那么肯定它根本就不是异步的。
似乎BackgroundWorker
非常适合冗长的计算:https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx#Examples
有没有办法将async
/ await
用于此目的?
答案 0 :(得分:2)
关键在于:
结合 Task.Run ,异步编程优于 BackgroundWorker用于CPU绑定操作,因为异步编程 将运行代码的协调详细信息与工作分开 Task.Run转移到线程池。
对于受CPU限制的工作(或IO绑定,无关紧要),Task.Run
将在与线程池不同的线程中运行您的代码,因此当前代码可以await
,因为它在不同的线程上运行。从第二个线程的透视开始,工作是同步的,但是从第一个线程的透视开始,工作是异步的。
第二件事是协调背景。我没有使用BackgroundWorker
,但根据我对文本的理解,它需要您手动检查工作是否完成,然后检索结果,或者传播异常,等等。在async/await
方法中,所有这些都为您提供。
总而言之,与async/await
方式相比,BackgroundWorker
方法似乎是一种更友好的可读方式。
编辑:
您无法在await
方法上使用non-awaitable
。 CPU绑定任务永远不能真正异步,因为需要计算某些东西,并且需要一个线程。当前线程将执行计算(阻塞),或者将其提供给另一个后台线程,因此当前线程不会阻塞和异步。
如果您熟悉Node.js,您会发现处理CPU绑定任务非常棘手,而且通常需要重构代码。
但是,作为用户,无论您正在等待的方法是真正的异步还是使用其他线程,您当前的线程都是异步的。
答案 1 :(得分:1)
在我看来,一系列异步函数最终必须等待一些超出程序控制范围的事情。
是;纯async
代码通常与I / O或其他“事件”一起使用(例如,定时器,通知或 - 通常 - 用户输入)。
当你的程序必须执行一些冗长的计算时会怎么样?
这不是async
的主要用例。但是,它是并行计算(Parallel
/ PLINQ)的一个很好的用例,或者如果工作量较小,则是线程池(Task.Run
)。
如果没有使用await,那么控制不会返回到调用函数,对吗?如果是这样的话那么肯定它根本就不是异步的。
这是正确的;没有async
的{{1}}方法实际上会同步运行。事实上,如果你在Visual Studio中键入它,编译器会给你一个警告,实际上说“这个方法将同步运行”。因为大部分时间都是错误的。
如果要在线程池线程上运行CPU绑定代码,请使用await
。您可以(并且通常应该)Task.Run
await
返回的任务,这允许调用者将线程池视为异步处理(即使它实际上只是在后台线程上同步运行) :
Task.Run
如果您想要并行处理的全部功能,您可以将其包装在await Task.Run(() => ...);
内:
Task.Run
看起来BackgroundWorker非常适合冗长的计算
不是真的。检索结果有点尴尬,因为很容易错过错误,结果不是类型安全的。而且,快速协调多个BGW变得困难。我有一个shows how BackgroundWorker
should essentially be considered obsoleted by Task.Run
的博客系列。