【发布时间】:2020-08-26 06:54:45
【问题描述】:
我有两个简单的异步方法。
static async Task First()
{
for (int i = 0; i < 2; i++)
{
await Task.Delay(200);
Console.WriteLine($"First method: {i}");
}
}
static async Task Second()
{
for (int i = 0; i < 2; i++)
{
await Task.Delay(200);
Console.WriteLine($"Second method: {i}");
}
}
并尝试以不同的方式调用它们,结果完全不同。我在下面向您展示每个呼叫及其响应消息。
-
首先简单地等待两者。
await First(); await Second(); //First method: 0 //First method: 1 //Second method: 0 //Second method: 1 -
其次,创建一个任务数组并通过迭代来调用它们。但结果是并行的。
var tasks = new Task[] { First(), Second() }; foreach (var t in tasks) { await Task.Run(async () => await t); } //Second method: 0 //First method: 0 //Second method: 1 //First method: 1 -
我称它们为 Parallel 的第三个示例,其结果是预期的
var tasks = new Task[] { First(), Second() };
Parallel.ForEach(tasks, async (t) => { await Task.Run(async () => await t); });
//First method: 0
//Second method: 0
//Second method: 1
//First method: 1
-
在最后一个示例中,我立即启动任务。结果也符合这里的预期。
var tasks = new Task[] { First(), Second() }; await Task.WhenAll(tasks); //First method: 0 //Second method: 0 //Second method: 1 //First method: 1
所以我的问题是:这四种调用异步任务的方法有什么区别?哪一个是最好的使用方式?
【问题讨论】:
-
“哪一个是最好的使用方式?” 你对“最好”的定义是什么? 1 和 4 至少不会创建不必要的线程和混合不特别“喜欢”彼此的概念。现在这取决于你需要什么。 1 将按顺序执行它们(
Second()将在First()完成后执行),4 将异步执行它们(不 等同于“并行”)并等待两者完成。 -
当我说“最佳”时,我指的是性能。第二个和第三个例子呢?为什么第二个例子不像第一个那样工作?为什么第二次迭代不等待第一次迭代和等待的Task?
-
好的,#2:你异步启动两个任务。这意味着它们可能(或可能不会)在两个线程上并行执行(不保证)。然后,您创建并等待另外两个任务,它们按顺序等待它们的执行。因此,这里的执行与等待它们的顺序是分开的。在第一个 sn-p 中,您执行
First()并立即等待它。这意味着它可能会或可能不会在单独的线程上执行,但无论如何,Second()只会在First()完成后启动。所以这里的执行顺序就是等待它们的顺序。 -
要评估性能更好的方法,我强烈建议您进行基准测试(请参阅 Benchmark.NET)。请注意,仅当
Second不依赖于First的结果时,#4 才有意义。 #1 或 #4 的执行方式可能取决于First和Second的实现、加载时、机器上您的应用程序旁边正在执行的内容……因此可能很难预测。 -
1, 4 - 好。 2,3 - 不好。参考:Asynchronous programming和Why 3 is bad。
标签: c# .net multithreading task parallels