在 c# 中以异步方式启动多个 "TASK"s
Starting multiple "TASK"s as async in c#
我有两个简单的异步方法。
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
所以我的问题是:这4种调用异步任务的方法有什么区别?其中哪一个是最好的使用方式?
您得到的结果可以用以下方式解释
- 你的方法 运行 sequentially,信不信由你
await
实际上意味着 await (因为没有涉及其他任务并且你的方法不是 运行未被观察到)
- 您的任务在您将它们添加到数组后立即开始,然后您将工作卸载到另一个任务并等待它,但是您 运行 工作量作为
async void
in-turn 运行s 未被观察到,因此每个外部任务立即 returns。结果的顺序取决于任务调度程序和最有可能是 2 个线程之间的竞争。
- 您正在使用 TPL 方法潜在地启动任务以启动任务,这些任务 运行 将您的工作量作为
async void
且未被观察到,因此 Parallel.Foreach
returns 立即地。结果的顺序取决于任务调度程序和最有可能是 2 个线程之间的竞争。
- 您正在开始 2 个任务,并且
await
都已完成。结果的顺序取决于任务调度程序和最有可能是 2 个线程之间的竞争。
Which of them is the best way to use?
这完全取决于您想要实现的目标...因为它们出于不同的原因而做的事情略有不同,而且有些方面完全多余,因此没有正确答案。
但是,对于您的示例方法,如果您想等待 2 个 async
工作负载异步完成,Task.WhenAll
似乎是最简洁和明智的方法
var tasks = new Task[] { First(), Second() };
await Task.WhenAll(tasks);
我有两个简单的异步方法。
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
所以我的问题是:这4种调用异步任务的方法有什么区别?其中哪一个是最好的使用方式?
您得到的结果可以用以下方式解释
- 你的方法 运行 sequentially,信不信由你
await
实际上意味着 await (因为没有涉及其他任务并且你的方法不是 运行未被观察到) - 您的任务在您将它们添加到数组后立即开始,然后您将工作卸载到另一个任务并等待它,但是您 运行 工作量作为
async void
in-turn 运行s 未被观察到,因此每个外部任务立即 returns。结果的顺序取决于任务调度程序和最有可能是 2 个线程之间的竞争。 - 您正在使用 TPL 方法潜在地启动任务以启动任务,这些任务 运行 将您的工作量作为
async void
且未被观察到,因此Parallel.Foreach
returns 立即地。结果的顺序取决于任务调度程序和最有可能是 2 个线程之间的竞争。 - 您正在开始 2 个任务,并且
await
都已完成。结果的顺序取决于任务调度程序和最有可能是 2 个线程之间的竞争。
Which of them is the best way to use?
这完全取决于您想要实现的目标...因为它们出于不同的原因而做的事情略有不同,而且有些方面完全多余,因此没有正确答案。
但是,对于您的示例方法,如果您想等待 2 个 async
工作负载异步完成,Task.WhenAll
似乎是最简洁和明智的方法
var tasks = new Task[] { First(), Second() };
await Task.WhenAll(tasks);