旋转等待 vs 睡眠等待。使用哪一个?

这样做有效吗

SpinWait.SpinUntil(() => myPredicate(), 10000)

超时10000毫秒

或者

在相同条件下使用 Thread.Sleep轮询是否更有效 例如,类似于下面 SleepWait函数的内容:

public bool SleepWait(int timeOut)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
while (!myPredicate() && stopwatch.ElapsedMilliseconds < timeOut)
{
Thread.Sleep(50)
}
return myPredicate()
}

我担心如果我们讨论超时超过1秒的话,SpinWait 的所有收益可能不是一个好的使用模式?这是一个有效的假设吗?

你更喜欢哪种方法? 为什么? 还有其他更好的方法吗?


更新 -变得更具体:

当 BlockingCollection Pulse 达到有限容量时,是否有办法使其成为睡眠线程?我宁愿避免像马克 · 格拉维尔建议的那样忙碌的等待。

75921 次浏览

最好的的方法是有一些机制让 主动侦测成为真(而不是被动轮询它的 Wait0为真) ; 这可以是任何类型的等待句柄,或者可能是 WaitTask,或者可能是 event,你可以订阅取消粘贴自己。当然,如果您执行这种“等待某些事情发生”的操作,那么 Wait1的效率就不如仅仅完成下一点工作 Wait2,这意味着: 您不需要使用线程来等待。TaskContinueWith来做这个,或者你也可以在 event被激活的时候做这个工作。取决于上下文,event可能是最简单的方法。然而,Task已经提供了您在这里讨论的大部分内容,包括“等待超时”和“回调”机制。

是的,旋转10秒钟并不好。如果你想使用类似于你当前代码的东西,如果你有理由期待一个短的延迟,但需要允许一个更长的-也许 SpinWait为(比如)20毫秒,并使用 Sleep为其余?


关于这个评论,下面是我如何钩住一个“它是否满了”的机制:

private readonly object syncLock = new object();
public bool WaitUntilFull(int timeout) {
if(CollectionIsFull) return true; // I'm assuming we can call this safely
lock(syncLock) {
if(CollectionIsFull) return true;
return Monitor.Wait(syncLock, timeout);
}
}

在“放回收集”代码中:

if(CollectionIsFull) {
lock(syncLock) {
if(CollectionIsFull) { // double-check with the lock
Monitor.PulseAll(syncLock);
}
}
}

进去。NET 4 SpinWait在生成之前执行10次 CPU 密集型旋转。但是在每个周期之后,它不返回到调用者 马上; 相反,它调用 Thread.SpinWait通过 CLR (本质上是 OS)在设定的时间段内旋转。这个时间周期最初是几十纳秒,但是随着每次迭代增加一倍,直到10次迭代完成。这使得在总时间消耗(CPU 密集型)阶段的清晰度/可预测性成为可能,系统可以根据条件(核心数量等)进行调优。如果 SpinWait在自旋收益阶段停留太长时间,它将周期性地休眠,以允许其他线程继续运行(更多信息请参见 J. Albahari 的博客)。这个过程保证使核心保持忙碌..。

因此,SpinWait将 CPU 密集型的自旋限制为一组迭代次数,在此之后,它将在每次自旋上产生其时间片(实际上是通过调用 Thread.YieldThread.Sleep) ,从而降低其资源消耗。它还将检测用户是否正在运行单个核心机器,如果是这种情况,则在每个循环中产生。

对于 Thread.Sleep,线程被阻塞。但是就 CPU 而言,这个进程不会像上面那样昂贵。