运行异步lambda时,使用Task.Run(func)或new Func <task>(func)()?

时间:2017-11-20 20:30:44

标签: c# asynchronous async-await

我想收集/运行任务,然后对它们进行Task.WhenAll

var tasks = new List<Task>();
foreach (var thing in things) {
  tasks.Add(Task.Run(async () => {
       // async stuff using thing
  }));
}

var stuffs = await Task.WhenAll(tasks);

在这里使用Task.Run是否正确<或者

tasks.Add(new Func<Task>(async () => {async stuff})());

还是别的什么呢?

2 个答案:

答案 0 :(得分:7)

这通常取决于您的异步工作的性质。如果您执行以下操作:

async () => {
   // some heavy CPU work here
   // then some IO
}

使用Task.Run会更好,因为否则&#34;繁重的CPU工作&#34;将在您当前的线程上运行,您将获得很少的并行化。

但是,这种情况比较少见,因为对于繁重的CPU工作的并行化还有其他工具。如果您有这样的事情:

async () => {
   // some async IO, like database call or web request
   // some processing
   // some more async IO
}

然后你不需要Task.Run。然后你可以使用你的第二种方法:

Func<Task> t = async () => {
     // some stuff
};
tasks.Add(t());

如果你在UI线程上(在WPF \ WinForms中)这样做 - 确保在异步委托中使用ConfigureAwait(false)以防止将控制返回到UI线程。

答案 1 :(得分:1)

除了Evk's response之外,了解并行性的潜在差异以及是否需要考虑线程安全性也很重要。

如果满足以下所有条件,则“ func”不需要是线程安全的:

  • 您使用new Func方法
  • 您不使用ConfigureAwait(false)
  • SynchronizationContext是一个同步上下文,这意味着它保证一次只能有一个线程可以执行代码(与旧式ASP.NET中一样)

否则,您需要考虑“ func”的线程安全性。