理解没有线程的async/await

本文关键字:async await 线程 | 更新日期: 2023-09-27 18:14:37

根据MSDN, asyncawait不创建新线程:

asyncawait关键字不会导致创建额外的线程。

考虑到这一点,我很难理解一些简单程序的控制流程。我的完整示例如下。注意,它需要Dataflow库,您可以从NuGet安装。
using System;
using System.Threading.Tasks.Dataflow;
namespace TaskSandbox
{
    class Program
    {
        static void Main(string[] args)
        {
            BufferBlock<int> bufferBlock = new BufferBlock<int>();
            Consume(bufferBlock);
            Produce(bufferBlock);
            Console.ReadLine();
        }
        static bool touched;
        static void Produce(ITargetBlock<int> target)
        {
            for (int i = 0; i < 5; i++)
            {
                Console.Error.WriteLine("Producing " + i);
                target.Post(i);
                Console.Error.WriteLine("Performing intensive computation");
                touched = false;
                for (int j = 0; j < 100000000; j++)
                    ;
                Console.Error.WriteLine("Finished intensive computation. Touched: " + touched);
            }
            target.Complete();
        }
        static async void Consume(ISourceBlock<int> source)
        {
            while (await source.OutputAvailableAsync())
            {
                touched = true;
                int received = source.Receive();
                Console.Error.WriteLine("Received " + received);
            }
        }
    }
}
输出:

Producing 0
Performing intensive computation
Received 0
Finished intensive computation. Touched: True
Producing 1
Performing intensive computation
Received 1
Finished intensive computation. Touched: True
Producing 2
Performing intensive computation
Received 2
Finished intensive computation. Touched: False
Producing 3
Performing intensive computation
Received 3
Finished intensive computation. Touched: False
Producing 4
Performing intensive computation
Received 4
Finished intensive computation. Touched: True

这似乎表明,当OutputAvailableAsync任务完成时,for循环正在运行时,Consume被赋予了控制权:

for (int j = 0; j < 100000000; j++)
    ;

这在线程模型中是不足为奇的。但是,如果不涉及额外的线程,Produce如何在for循环中放弃控制呢?

理解没有线程的async/await

如果不涉及额外的线程,如何在for循环的中间产生yield控制?

谁说不涉及额外的线程?你所说的事实是:

async和await关键字不会导致创建额外的线程。

这是绝对正确的。您的程序包含片段

target.Post(i);
await source.OutputAvailableAsync())

我的猜测是target.Post(i)source.OutputAvailableAsync()的调用创建了一个线程。await不产生线程;await所做的只是将方法的剩余部分分配给调用返回的任务的延续,然后将控制权返回给调用者。如果该任务产生一个线程来完成它的工作,那就是它的业务。

await只是另一个控制流;当然,这是一个非常复杂的控制流,但毕竟是一个控制流。它不是创建线程的语法糖;

控件处理在同一个线程内完成,因此当循环运行时,Consume方法不在,反之亦然。如果您使用线程,则不一定是这样,实际上您希望两者并发运行。

它们在同一个线程中的事实并不意味着控制不能从代码的一部分传递到另一部分,. net(和其他框架,AFAIK)处理得很顺利,每个部分在自己的上下文中运行没有任何问题。

然而,在一个线程中运行这两个东西意味着当Consume运行时,循环将"挂起"。如果Consume花的时间太长,这正是用户可能会感觉到的。这就是为什么许多不熟悉windows窗体的程序员会惊讶地发现,一次用太多的信息填充他们的GUI控件会导致他们的窗体挂起,有时甚至是空白——如果您不使用后台工作线程,刷新屏幕的线程就是控制逻辑运行的线程。