【问题标题】:Async thread body loop, It just works, but how?异步线程体循环,它只是工作,但如何?
【发布时间】:2015-05-05 05:22:12
【问题描述】:

我刚刚测试了一些我确信会惨遭失败的东西,但令我惊讶的是,它完美无缺,并向我自己证明我仍然对 async-await 的工作原理感到困惑。

我创建了一个线程,传递了一个async void 委托作为线程的主体。 这是我的代码的过度简化:

var thread = new Thread( async () => {
   while( true ) {
      await SomeLengthyTask();
      ...
   }
});
thread.Start();
thread.Join();

据我了解,当执行到await 关键字时,方法会隐式返回,在这种情况下是循环线程的主体,而其余代码被包装在回调继续中。

由于这个事实,我很确定线程会在await 产生执行后立即终止,但事实并非如此!

有人知道这个魔法实际上是如何实现的吗?async 功能被剥离并且 async 同步等待还是 CLR 正在执行一些黑魔法使其能够恢复因await 而产生的线程?

【问题讨论】:

    标签: c# .net multithreading asynchronous async-await


    【解决方案1】:

    线程确实很快终止了。

    但是由于Thread 构造函数不接受async lambda,所以你得到了一个async void 委托。

    原始线程将结束,后续(await 之后的其余部分)将发布到ThreadPool 并最终在另一个线程上运行。

    您可以通过检查线程 id 来测试:

    var thread = new Thread(async () =>
    {
        while (true)
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            await SomeLengthyTask();
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        }
    });
    thread.Start();
    thread.Join();
    
    Console.ReadLine();
    

    输出:

    3
    5
    5
    ...
    5
    

    为了让这个例子更简单,我们假设你有这个Run 方法:

    void Run(Action action)
    {
        action();
    }
    

    然后你用你的 async 代表调用它

    Run(async () => 
    {
        while(true) 
        {
          await SomeLengthyTask();
          ...
        }
    });
    

    Run 的执行将在到达第一个await 并返回时几乎立即完成。 async 委托的其余部分将继续在 ThreadPool 上使用另一个线程。


    通常,每次在执行 async 方法时到达 await 时,线程都会丢失,并且继续(等待的任务完成后的其余部分)将被发布到 ThreadPool(除非有 SynchronizationContext存在,就像在 UI 线程中一样)。它可能会在同一个线程上执行(如我的示例中的 5),但也可能不会。

    在您的情况下,您明确创建的线程不是ThreadPool 的一部分,因此它肯定会被终止,其余的将在不同的线程上运行。

    【讨论】:

    • @BlueStrat 不,它确实发生了。这实际上就是我在示例中添加Console.ReadLine 的原因。没有它,我的控制台应用程序在到达有趣的部分之前就结束了。
    • @BlueStrat 此处的 Thread 没有什么特别之处。 async 方法只是不在同一个线程上运行。他们可能会这样做,但只要没有 SynchronizationContext,他们就会在 ThreadPool 上运行每个同步部分
    • 很好的答案,感谢您的洞察力-我想这是维护线程循环的一种非常昂贵的方式;)
    • @BlueStrat 如果您使用Task.Run 而不是创建自己的线程,它实际上可以非常可扩展。这取决于async 是您的while 的内容。如果您做某事并等待一分钟然后再做一次等。这比同步等待要好得多。 stackoverflow.com/a/27189890/885318
    • @BlueStrat 实际上,这是一种非常常见的模式,效果很好。主要问题首先是await 是否有意义——如果您正在执行一些 I/O 或Task.Delay 或类似的事情,它应该使调度线程池工作项的开销相形见绌。但是创建线程完全没用 - 只需使用Task.Run。它用于网络服务器中的主连接接受循环之类的东西 - async void 具有 while (true) { var s = await listener.AcceptAsync(); HandleAcceptAsync(s); } 的方法(HandleAcceptAsync 也是 async void 方法)。
    猜你喜欢
    • 2013-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-27
    • 2014-08-06
    • 1970-01-01
    • 2016-02-01
    相关资源
    最近更新 更多