根据MSDN的说法, async
和await
不会创建新线程:
async
和await
关键字不会导致额外的线程被创建。
考虑到这一点,我很难理解一些简单程序的控制流程。下面是我的完整示例。请注意,它需要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
这似乎表明在
for
循环运行时,Consume
获得控制权,因为 OutputAvailableAsync
任务已经完成:for (int j = 0; j < 100000000; j++)
;
在线程模型中,这并不令人惊讶。但如果没有涉及其他线程,那么在
for
循环的中间,如何使 Produce
放弃控制权呢?
await
调用的回调都会转到SynchronizationContext.Default
,即线程池,因此在执行此程序时实际上有两个线程在某些时候运行。为了防止创建额外的线程,您需要创建自定义同步上下文并设置它。如果这样做,您会发现在生产结束之前不会调用“Received”调用,因为在生产过程中不放弃控制权。 - Servyawait
不一定涉及线程的创建。您可以以不创建线程的方式使用它。您只是还没有这样做。此外请注意,它仅使用线程池线程来运行回调。当线程池线程没有任务时,它并不会阻塞,实际上它什么也不做并被释放回线程池,可以处理另一个请求。这很重要,因为它意味着您不会有100个线程池线程阻塞在那里等待IO完成。 - Servyasync
/await
介绍很有帮助。我试图在一篇文章中涵盖所有基础知识和相关细节。 - Stephen Cleary