【问题标题】:Understanding async/await without threads [duplicate]了解没有线程的异步/等待[重复]
【发布时间】:2013-05-16 18:16:30
【问题描述】:

According to MSDNasyncawait 不创建新线程:

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

考虑到这一点,我很难理解一些简单程序的控制流程。我的完整示例如下。请注意,它需要 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

这似乎表明Consumefor 循环运行时获得了控制权,因为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/await intro 很有帮助。我试图在一篇文章中介绍基础知识(以及所有相关细节)。

标签: c# .net multithreading task-parallel-library async-await


【解决方案1】:

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

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

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

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

target.Post(i);

await source.OutputAvailableAsync())

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

await 只是另一个控制流;可以肯定的是,这是一个非常复杂的控制流,但仍然是一个控制流。它不是创建线程的语法糖;它是为任务分配延续的语法糖。

【讨论】:

  • 实际上,这些调用都没有创建线程。 @Servy 是正确的,因为 await 运算符(更具体地说,await 运算符生成的代码使用的任务等待器)使用默认的 SynchronizationContext 来安排线程池线程上的方法继续。
  • @StephenCleary:啊,很好。感谢您的来信。
【解决方案2】:

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

它们在同一个线程中的事实并不意味着控制不能从代码的一部分传递到另一个。 .NET(以及所有其他框架,AFAIK)可以顺利处理这一点,并且每个部分都使用自己的上下文运行,没有任何问题。

然而,在一个线程中运行这两个东西意味着当Consume 正在运行时,循环将“挂起”。如果Consume 花费的时间太长,那么这正是用户可能感知到的。这就是为什么许多刚接触 Windows 窗体的程序员会惊讶于一次用太多信息填充他们的 GUI 控件会导致他们的窗体挂起,有时会出现空白 - 刷新屏幕的线程与运行控制逻辑的线程相同,如果你'没有使用后台工作线程。

【讨论】:

  • 是什么导致控制权传递给ConsumeProduce 没有明确让出控制权? .NET 是否注意到Produce 已经运行了很长时间,并将控制权交给Consume(类似于线程模型中操作系统的角色)?
  • 其实我觉得 Servy 在 cmets 中的答案比我的好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-09
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
  • 2020-07-08
  • 1970-01-01
相关资源
最近更新 更多