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

标签 c# multithreading async-await task-parallel-library threadpool

回答问题:Task.Yield - real usages? 我建议使用 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();
    }

但是一位用户写道

I don't think using Task.Yield to overcome ThreadPool starvation while implementing producer/consumer pattern is a good idea. I suggest you ask a separate question if you want to go into details as to why.

有人知道,为什么这不是个好主意?

最佳答案

您的问题的评论中有一些要点。作为您引用的用户,我只想总结一下:使用正确的工具来完成工作。

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

也就是说,在一些专用的池外线程上使用具有有限并行性的自定义 TaskScheduler 可能会是一个不同的东西。至少,await continuations 会发布在同一个线程上,这应该有助于减少切换开销。这让我想起了我前一段时间试图用 ThreadAffinityTaskScheduler 解决的另一个问题。 .

不过,根据特定情况,通常最好使用现有的行之有效且经过测试的工具。仅举几例:Parallel Class , TPL Dataflow , System.Threading.Channels , Reactive Extensions .

还有一整套现有的工业级解决方案来处理发布-订阅模式(RabbitMQ、PubNub、Redis、Azure Service Bus、Firebase Cloud Messaging (FCM)、Amazon Simple Queue Service (SQS) 等)。

关于c# - 在实现生产者/消费者模式时使用 Task.Yield 克服 ThreadPool 饥饿,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53263258/

相关文章:

c# - 在 LINQ 中加入和分组

c++ - 在多线程上下文中正确处理 GetLastError(和其他)

multithreading - 更新线程变量 (c++)

c# - ProgressRing 变为非事件状态

c# - 无法将类型 'string' 隐式转换为 'System.Threading.Tasks.Task<string>'

c# - 如何在 ASP.NET MVC 中通过 Ajax 提交表单?

c# - 如何根据DataType组合 "collectionResult"

c# - 如何使用脚本从远程服务器中的MS Queue清除消息?

c - PThread 地址空间

c# - 在 C# 中使用 Task.FromResult v/s await