【问题标题】:Starting multiple "TASK"s as async in c# [closed]在 c# 中以异步方式启动多个“TASK”[关闭]
【发布时间】: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}");
    }
}

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

  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
    

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

【问题讨论】:

  • “哪一个是最好的使用方式?” 你对“最好”的定义是什么? 1 和 4 至少不会创建不必要的线程和混合不特别“喜欢”彼此的概念。现在这取决于你需要什么。 1 将按顺序执行它们(Second() 将在First() 完成后执行),4 将异步执行它们( 等同于“并行”)并等待两者完成。
  • 当我说“最佳”时,我指的是性能。第二个和第三个例子呢?为什么第二个例子不像第一个那样工作?为什么第二次迭代不等待第一次迭代和等待的Task?
  • 好的,#2:你异步启动两个任务。这意味着它们可能(或可能不会)在两个线程上并行执行(不保证)。然后,您创建并等待另外两个任务,它们按顺序等待它们的执行。因此,这里的执行与等待它们的顺序是分开的。在第一个 sn-p 中,您执行 First() 并立即等待它。这意味着它可能会或可能不会在单独的线程上执行,但无论如何,Second() 只会在 First() 完成后启动。所以这里的执行顺序就是等待它们的顺序。
  • 要评估性能更好的方法,我强烈建议您进行基准测试(请参阅 Benchmark.NET)。请注意,仅当 Second 不依赖于 First 的结果时,#4 才有意义。 #1 或 #4 的执行方式可能取决于 FirstSecond 的实现、加载时、机器上您的应用程序旁边正在执行的内容……因此可能很难预测。
  • 1, 4 - 好。 2,3 - 不好。参考:Asynchronous programmingWhy 3 is bad

标签: c# .net multithreading task parallels


【解决方案1】:

你得到的结果可以用以下来解释

  1. 您的方法按顺序运行,信不信由你await 实际上意味着 await(因为不涉及其他任务并且您的方法不会在未观察到的情况下运行)
  2. 您的任务在将它们添加到阵列后立即开始,然后您将工作卸载到另一个任务并等待,但是您将工作负载作为 async void 运行,而这又在未观察到的情况下运行,因此每个外部任务立即返回。结果的顺序取决于任务调度程序以及最有可能是 2 个线程之间的竞争。
  3. 您正在使用 TPL 方法潜在地启动任务以启动以 async void 运行您的工作负载且未被观察到的任务,因此 Parallel.Foreach 立即返回。结果的顺序取决于任务调度程序以及最有可能是 2 个线程之间的竞争。
  4. 您正在开始 2 个任务,await 完成了这两个任务。结果的顺序取决于任务调度程序以及最有可能是 2 个线程之间的竞争。

哪一个是最好的使用方式?

这完全取决于你想要达到的目标......因为他们都出于不同的原因做的事情略有不同,而且有些方面完全多余,所以没有正确的答案。

但是,对于您的示例方法,Task.WhenAll 似乎是最简洁和理智的方法,如果您想等待 2 个 async 工作负载异步完成

var tasks = new Task[] { First(), Second() };
await Task.WhenAll(tasks);

【讨论】:

  • 关于2的解释,不涉及async voidTask.Run 理解异步委托。
猜你喜欢
  • 2021-10-15
  • 2023-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-09
  • 2023-03-26
  • 2011-11-07
  • 2011-03-22
相关资源
最近更新 更多