使用 Task.Yield 在实现生产者/消费者模式时克服 ThreadPool 饥饿

Max*_*m T 5 c# multithreading threadpool task-parallel-library async-await

回答问题:Task.Yield - 实际用途? 我建议使用 Task.Yield 允许池线程被其他任务重用。在这样的模式中:

  CancellationTokenSource cts;
  void Start()
  {
        cts = new CancellationTokenSource();

        // run async operation
        var task = Task.Run(() => SomeWork(cts.Token), cts.Token);
        // wait for completion
        // after the completion handle the result/ cancellation/ errors
    }

    async Task<int> SomeWork(CancellationToken cancellationToken)
    {
        int result = 0;

        bool loopAgain = true;
        while (loopAgain)
        {
            // do something ... means a substantial work or a micro batch here - not processing a single byte

            loopAgain = /* check for loop end && */  cancellationToken.IsCancellationRequested;
            if (loopAgain) {
                // reschedule  the task to the threadpool and free this thread for other waiting tasks
                await Task.Yield();
            }
        }
        cancellationToken.ThrowIfCancellationRequested();
        return result;
    }

    void Cancel()
    {
        // request cancelation
        cts.Cancel();
    }
Run Code Online (Sandbox Code Playgroud)

但是一位用户写道

我不认为在实现生产者/消费者模式的同时使用 Task.Yield 来克服 ThreadPool 饥饿是一个好主意。如果您想详细了解原因,我建议您提出一个单独的问题。

有谁知道,为什么不是一个好主意?

nos*_*tio 5

对你的问题的评论中留下了一些很好的观点。作为您引用的用户,我只想总结一下:使用正确的工具来完成工作。

使用ThreadPool感觉不是执行多个连续的 CPU 密集型任务的正确工具,即使您尝试通过将它们转变为状态机来组织一些协作执行,从而为彼此提供 CPU 时间await Task.Yield()。线程切换的开销相当大;通过await Task.Yield()在紧密的循环中执行,您会增加大量的开销。此外,您永远不应该接管整个ThreadPool.NET 框架(和底层操作系统进程)可能需要它来做其他事情。与此相关的是,TPL 甚至可以选择TaskCreationOptions.LongRunning请求不在线程上运行任务(而是在幕后ThreadPool创建一个普通线程)。new Thread()

也就是说,在一些专用的、池外线程上使用具有有限并行性的自定义,并且对单个长时间运行的任务具有线程关联性 可能是另一回事。至少,延续将被发布在同一个线程上,这应该有助于减少切换开销。这让我想起了不久前我试图解决的另一个问题。TaskSchedulerawaitThreadAffinityTaskScheduler

不过,根据特定场景,通常最好使用现有的成熟且经过测试的工具。仅举几例:Parallel ClassTPL DataflowSystem.Threading.ChannelsReactive Extensions

还有一整套现有的工业级解决方案来处理发布-订阅模式(RabbitMQ、PubNub、Redis、Azure 服务总线、Firebase 云消息传递 (FCM)、Amazon 简单队列服务 (SQS) 等)。