在 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}");
    }
}

并尝试以不同的方式调用它们,结果完全不同。我在下面向您展示每个调用及其响应消息。

  1. 先简单等待一下。

         await First();
         await Second();
    
         //First method: 0
         //First method: 1
         //Second method: 0
         //Second method: 1
    
  2. 其次,创建任务数组并通过迭代调用它们。但是结果是并行的。

       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
    
  3. 第三个例子我称他们为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
  1. 在最后一个示例中,我立即启动了任务。结果也符合预期。

         var tasks = new Task[] { First(), Second() };
         await Task.WhenAll(tasks);
    
         //First method: 0
         //Second method: 0
         //Second method: 1
         //First method: 1
    

所以我的问题是:这4种调用异步任务的方法有什么区别?其中哪一个是最好的使用方式?

您得到的结果可以用以下方式解释

  1. 你的方法 运行 sequentially,信不信由你 await 实际上意味着 await (因为没有涉及其他任务并且你的方法不是 运行未被观察到)
  2. 您的任务在您将它们添加到数组后立即开始,然后您将工作卸载到另一个任务并等待它,但是您 运行 工作量作为 async void in-turn 运行s 未被观察到,因此每个外部任务立即 returns。结果的顺序取决于任务调度程序和最有可能是 2 个线程之间的竞争。
  3. 您正在使用 TPL 方法潜在地启动任务以启动任务,这些任务 运行 将您的工作量作为 async void 且未被观察到,因此 Parallel.Foreach returns 立即地。结果的顺序取决于任务调度程序和最有可能是 2 个线程之间的竞争。
  4. 您正在开始 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);