此代码消耗近零CPU (i5系列)。
public void SpinWait() {
for (int i = 0; i < 10000; i++)
{
Task.Factory.StartNew(() =>
{
var sw = new SpinWait();
while (true)
{
sw.SpinOnce();
}
});
}
}在我的代码中,当旋转是正确的(5次Mops)时,与SemaphoreSlim相比,性能差异是3倍或更多。然而,我担心的是长期使用它.标准的建议是实施两阶段等待操作。我可以检查NextSpinWillYield属性,并引入一个counter+reset来增加默认的自旋迭代,而不是退回到信号量。
但是,仅仅使用SpinWait.SpinOnce来长期等待有什么坏处呢?我已经看过了它的实现,它在需要时会适当地产生效果。它使用的是Thread.SpinWait,在现代CPU上使用暂停指令,根据英特尔的说法是非常有效的。
我在监视Task时发现的一个问题是,如果由于默认的ThreadPool算法而逐渐增加线程数(在所有任务繁忙时,它每秒钟添加一个线程)。这可以通过使用ThreadPool.SetMaxThreads来解决,然后线程数是固定的,并且CPU的使用率仍然接近于零。
如果长期等待任务的数量是有限的,那么使用SpinWait.SpinOnce进行长期等待的其他缺陷是什么。它依赖于CPU家族,操作系统,.NET版本吗?
(为了澄清:我仍然会实现两阶段等待,我只是好奇,为什么不一直使用SpinOnce呢?)
发布于 2016-04-14 10:37:39
好的,反面就是你看到的,你的代码占据了一个线程而没有完成任何事情。防止其他代码运行,并迫使线程池管理器对其进行处理。修补ThreadPool.SetMaxThreads()只是一种绷带,它很可能是大量出血的伤口,只有当你需要赶上飞机回家时才使用它。
只有在您很好地保证这样做比线程上下文切换更有效的时,才应该尝试旋转。这意味着您必须确保线程可以在大约10,000个cpu周期内或更短的时间内继续。这仅仅是5微秒,给予或接受,比大多数程序员所认为的“长期”要少得多。
使用将触发线程上下文切换的同步对象。或者lock关键字。
这不仅会产生处理器,使其他等待线程能够完成它们的工作,从而完成更多的工作,而且它还为OS线程调度程序提供了一个极好的提示。发出信号的同步对象将提高线程的优先级,因此很可能下一个得到处理器。
https://stackoverflow.com/questions/36618304
复制相似问题