【问题标题】:Is there overhead when calling await on already split context?在已经拆分的上下文上调用 await 时是否有开销?
【发布时间】:2016-02-25 01:36:38
【问题描述】:

编辑:我的问题没有真正的意义,因为我误解了任务的工作方式。我已将 stephen cleary 的答案标记为已接受,因为它为我指明了学习错误的方向。

我经常看到这样的代码:

public async Task DoSomething(){
     await DoSomethingElse();
     await DoSoManyThings();
     await DoEverything();
}

现在我看到的方式是第一个等待启动第二个线程并将控制权返回给调用线程。当 await 完成时,调用第二个 await。现在这是我可能感到困惑的地方。第二个等待拆分上下文/创建新线程,另一个线程消失(返回给调用者)。这有开销吗?如果有,多少钱?最好让我的代码如下,以避免无缘无故地创建新线程:

public async Task DoSomething(){
     await DoSomethingElse();
      DoSoManyThings();
      DoEverything();
}

这是一个完整的代码示例,根据要求显示了我的意思。 该循环用于确保等待的代码不会立即完成。两种测试功能的输出相同。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestApplication
{
    public class Program
    {
        static void Main(string[] args)
        {
            Program test = new Program();
            Task firstTest = test.StartMultipleAwaitTest();
            firstTest.Wait();
            test.StartSingleAwaitTest();
            Console.ReadLine();
        }

        public async Task StartSingleAwaitTest() 
        {
            Console.WriteLine("\nStarting second test");
            await FirstTask();
            SecondTask();
            ThirdTask();
            Console.WriteLine("End");
        }

        public async Task StartMultipleAwaitTest() 
        {
            Console.WriteLine("Start");
            await FirstTask();
            await SecondTask();
            await ThirdTask();
            Console.WriteLine("End");
        }

        public async Task FirstTask() 
        {
            Console.WriteLine("Start First task");
            for (int i = 0; i < 10000000; i++) { }
            Console.WriteLine("End Firt task");
        }

        public async Task SecondTask()
        {
            Console.WriteLine("Start Second task");
            for (int i = 0; i < 10000000; i++) { }
            Console.WriteLine("End Second task");
        }

        public async Task ThirdTask()
        {
            Console.WriteLine("Start Third task");
            for (int i = 0; i < 10000000; i++) { }
            Console.WriteLine("End Third task");
        }
    }
}

【问题讨论】:

  • 如果 DoEveryThing 需要来自 DoSoManyThings() 的输出,则第二种方法可能不起作用。是的,有开销,但很少。每个 Program 和 Process 都有一些开销,线程的开销大致相同。
  • 据我了解,我的两个代码示例都应该以完全相同的方式工作。也就是说,在第一个函数完成之前,第二个函数永远不会被调用,而第三个函数在第二个完成之前永远不会被调用。你只是猜测开销是一样的,你有什么理由/来源吗?
  • "第一个 await 启动第二个线程并将控制权返回给调用线程" -- 仅仅因为某个东西是 async,并不意味着它会创建一个新的线。假设被调用的方法没有变化并且它们确实是异步的,这两个示例工作方式相同;第 2 种,DoEverything() 方法将在DoSoManyThings() 启动的异步操作完成之前被调用,而第 1 种方法直到之后才会被调用。
  • 我也不知道你说的“拆分上下文”是什么意思;假设一个非默认同步上下文,每个await 将在操作完成时返回到原始上下文。无论如何,你的问题太宽泛了。请将您的场景缩小到您可以提供 a good, minimal, complete code example 以清楚显示您的要求的程度。
  • 添加了完整示例。我说拆分上下文是因为新线程在新上下文上运行,尽管可能不是问我问题的最清楚的方式。

标签: c# multithreading asynchronous async-await overhead


【解决方案1】:

第一个等待启动第二个线程并将控制权返回给调用线程。

没有。 await 最强调 启动第二个线程。我有一篇描述 what await does do in detail 的博文。

两个测试函数的输出相同。

输出相同的原因是因为在这两种情况下,所有函数都是同步运行的。注意你的编译器警告;在这种情况下,编译器会在您的 FirstTask 方法(和朋友)上向您发出警告,说明这些方法将同步运行。

StartMultipleAwaitTest 测试中实际发生的情况是,在await 甚至检查Task 之前,这些方法已经运行完成。同样,在StartSingleAwaitTest测试中,方法也同步运行完成,然后返回StartSingleAwaitTest

更真实的测试(即在异步方法中执行异步工作)将为您提供更真实的结果:

public async Task FirstTask() 
{
  Console.WriteLine("Start First task");
  await Task.Delay(TimeSpan.FromSeconds(2));
  Console.WriteLine("End Firt task");
}

public async Task SecondTask()
{
  Console.WriteLine("Start Second task");
  await Task.Delay(TimeSpan.FromSeconds(3));
  Console.WriteLine("End Second task");
}

public async Task ThirdTask()
{
  Console.WriteLine("Start Third task");
  await Task.Delay(TimeSpan.FromSeconds(2));
  Console.WriteLine("End Third task");
}

【讨论】:

  • 感谢您的回复!我对任务的理解存在一些重大缺陷,您的博客文章和其他一些资源帮助我清除了它。我已将您的答案标记为正确,尽管它并没有真正回答我的问题,尽管这是因为我的问题没有真正的意义!
猜你喜欢
  • 2015-01-14
  • 1970-01-01
  • 2015-11-13
  • 2011-03-04
  • 2016-06-18
  • 2018-06-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多