SpinWait与睡眠等待。哪一个使用?

时间:2012-03-15 11:44:42

标签: c# multithreading

是否有效
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在达到有界容量时成为睡眠线程?正如Marc Gravel建议的那样,我宁愿避免忙碌等待。

2 个答案:

答案 0 :(得分:117)

在.NET 4 SpinWait中,在屈服之前执行10次迭代的CPU密集型旋转。但是在每个周期之后它不会立即返回到调用者 ;相反,它调用Thread.SpinWait通过CLR(本质上是OS)旋转一段时间。该时间段最初为几十纳秒,但每次迭代加倍,直到10次迭代完成。这使得旋转(CPU密集型)阶段所花费的总时间的清晰度/可预测性成为可能,系统可以根据条件(核心数等)进行调整。如果SpinWait在旋转产生阶段保持太长时间,它将定期休眠以允许其他线程继续(有关更多信息,请参阅J. Albahari's blog)。保证这个过程保持核心忙......

因此,SpinWait将CPU密集型旋转限制为设定的迭代次数,然后在每次旋转时产生时间片(通过实际调用Thread.YieldThread.Sleep),降低资源消耗。如果是这种情况,它还将检测用户是否正在运行单个核心机器并在每个周期产生。

使用Thread.Sleep线程被阻止。但是这个过程在CPU方面不会像上面那样昂贵。

答案 1 :(得分:43)

最佳方法是有一些机制主动检测事物变为真(而不是被动轮询变为为真) ;这可以是任何类型的等待句柄,也可以是Task Wait,或者event,您可以订阅以自行解锁。当然,如果你做那种“等到事情发生”,那么仍然不如仅仅完成下一部分工作作为回调那么高效,这意味着:您不需要使用线程等待。 Task对此有ContinueWith,或者您可以在event被解雇时完成工作。 event可能是最简单的方法,具体取决于上下文。但是,Task已经提供了你在这里谈论的大部分内容,包括“等待超时”和“回调”机制。

是的,旋转10秒并不是很好。如果您想使用类似当前代码的内容,并且您有理由期望短暂的延迟,但需要允许更长的延迟 - 也许SpinWait(例如)20ms,并使用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);
        }
    }
}