【发布时间】:2019-04-15 05:43:44
【问题描述】:
回答问题: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();
}
但一位用户写道
我不认为使用 Task.Yield 来克服 ThreadPool 饥饿,而 实施生产者/消费者模式是个好主意。我建议你 如果您想详细说明原因,请提出一个单独的问题。
任何人都知道,为什么不是一个好主意?
【问题讨论】:
-
我对原始评论者的动机没有确定的想法,但您应该尽量避免等待数据到达的繁忙循环,而是应该使用允许您触发处理的机制。
-
我认为热循环是不好的有或没有将异步添加到混合中 - 如果它是
await Task.Delay(50)或其他东西,我会原谅它更多,但是:使用异步激活而不是这样检查会更好;有新的“通道”API,例如 (nuget.org/packages/System.Threading.Channels) - 设计 用于异步生产者/消费者场景 -
@MaximT 确实 - 这是我在 SE.Redis 中用于有序消息队列的东西:github.com/StackExchange/StackExchange.Redis/blob/master/src/…
-
这与线程池管理器试图做的事情完全相反。它努力将活动 tp 线程的数量限制在理想数量,以减少上下文切换开销。当您使用 Task.Yield 时,您会添加上下文切换开销。如果您有太多无法有效执行代码的 tp 线程(阻塞太多),请使用 SetMinThreads()。
-
嗯,当然它必须这样工作。再多的负担得起的钱也不会为您购买具有一千个处理器内核的机器。你不能用这么多的工作来抨击线程池并期待即时的魔法。这些是属于问题的重要细节。
标签: c# multithreading async-await task-parallel-library threadpool