【发布时间】:2013-05-16 18:16:30
【问题描述】:
According to MSDN、async 和 await 不创建新线程:
async和await关键字不会导致创建额外的线程。
考虑到这一点,我很难理解一些简单程序的控制流程。我的完整示例如下。请注意,它需要 Dataflow library,您可以从 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
这似乎表明Consume 在for 循环运行时获得了控制权,因为OutputAvailableAsync 任务完成:
for (int j = 0; j < 100000000; j++)
;
这在线程模型中不足为奇。但是如果不涉及额外的线程,Produce如何在for循环中间产生控制?
【问题讨论】:
-
@I4V:该问题的答案表明“所有阻塞操作都必须使用 async/await 模型显式地产生控制权。”但在我的问题中,控制权从
Produce传递到Consume,而Produce没有明确让出控制权。这是我感到困惑的部分。 -
@Matthew 因为这是一个控制台应用程序,所以没有
SynchronizationContext,这意味着来自await调用的所有回调都转到SynchronizationContext.Default,这是线程池,所以在技术上存在实际上是在该程序执行期间有时运行的两个线程。为了防止创建额外的线程,您需要创建自己的自定义同步上下文并进行设置。如果你这样做了,你会看到Recieved调用不会被调用,直到所有的生产都特别完成,因为你在生产时没有让出控制权。 -
@Servy:如果涉及多个线程,为什么 MSDN 声称“特别是,对于 IO 绑定操作,这种方法比 BackgroundWorker 更好,因为......你不必防范比赛条件”?我编辑了我的示例以添加一个简单的竞争条件。
-
@Matthew 使用
await并不必然涉及线程的创建。您可以以永远不会创建线程的方式使用它。你根本没有这样做。另请注意,它使用线程池线程只是为了运行回调。那个线程池线程在无事可做时不是阻塞,它实际上什么都不做并被释放回池中;它能够处理另一个请求。这很重要,因为这意味着您没有 100 个线程池线程坐在那里阻塞,等待 IO 完成。 -
@Matthew 您可能会发现 my
async/awaitintro 很有帮助。我试图在一篇文章中介绍基础知识(以及所有相关细节)。
标签: c# .net multithreading task-parallel-library async-await