回答问题: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饥饿 实施生产者/消费者模式是一个好主意。我建议你 问一个单独的问题,是否要详细说明原因。
任何人都知道,为什么不是一个好主意?
答案 0 :(得分:1)
在您的问题的评论中还有一些要点。作为您引用的用户,我只想总结一下:为工作使用正确的工具。
使用ThreadPool
似乎不是执行多个连续的CPU约束任务的正确工具,即使您尝试通过将协作转化为状态机来组织一些协作执行,这些状态机也会与{ {1}}。线程切换相当昂贵。通过在紧密循环上执行await Task.Yield()
,会增加大量开销。此外,您绝不应该接管整个await Task.Yield()
,因为.NET框架(和底层OS进程)可能需要它来做其他事情。与此相关的是,TPL甚至具有ThreadPool
选项,该选项请求不要在TaskCreationOptions.LongRunning
线程上运行任务(相反,它创建了一个普通线程,其中ThreadPool
位于幕后)。 / p>
也就是说,在某些专用的,池外线程上使用并行性有限的 custom new Thread()
可能对各个长期运行的任务具有线程亲和力 他们是另一回事。至少,TaskScheduler
延续将发布在同一线程上,这将有助于减少切换开销。这使我想起了我之前尝试使用ThreadAffinityTaskScheduler
解决的另一个问题。
不过,根据特定的情况,通常最好使用现有的成熟工具和经过测试的工具。仅举几例:Parallel Class,TPL Dataflow,System.Threading.Channels,Reactive Extensions。
还有各种现有的行业实力解决方案可以处理发布-订阅模式(RabbitMQ,PubNub,Redis,Azure服务总线,Firebase云消息传递(FCM),Amazon Simple Queue Service(SQS)等)。
答案 1 :(得分:0)
与其他用户就此问题进行了一些辩论之后,他们担心上下文切换及其对性能的影响。 我知道他们在担心什么。
但是我的意思是:在循环内做点什么……是一项艰巨的任务-通常采用消息处理程序的形式,该处理程序从队列中读取消息并进行处理。消息处理程序通常是用户定义的,消息总线使用某种调度程序执行它们。用户可以实现一个同步执行的处理程序(没人知道用户会做什么),而没有Task.Yield的执行程序将阻塞线程以循环处理这些同步任务。
不要空洞,我向github添加了测试:https://github.com/BBGONE/TestThreadAffinity 他们将ThreadAffinityTaskScheduler,具有BlockingCollection的.NET ThreadScheduler和具有Threading.Channels的.NET ThreadScheduler进行了比较。
测试表明,对于超短作业,性能下降为 约15%。在不降低性能(甚至很小)的情况下使用Task.Yield -不要使用非常短的任务,如果任务太短,则将较短的任务组合成更大的批处理。
[上下文切换的价格] = [上下文切换持续时间] /([作业持续时间] + [上下文切换持续时间])。
在这种情况下,任务切换对性能的影响可以忽略不计。但是它增加了系统更好的任务协作和响应能力。
对于长时间运行的任务,最好使用自定义的调度程序,该调度程序在自己的专用线程池(例如WorkStealingTaskScheduler)上执行任务。
对于混合作业-可能包含不同的部分-短期运行的CPU绑定,异步和长期运行的代码部分。最好将任务分为子任务。
<input type="text" v-model="keyword" class="form-control" id="telefoon" placeholder="Search...">
<script>
var app = new Vue({
el: '#app',
data: {
keyword: '',
samsungList: []
},
computed: {
samsungFilteredList() {
return this.samsungList.filter((samsung) => {
return samsung.title.toLowerCase().includes(this.keyword.toLowerCase());
});
}
}
});
</script>