是否有效
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建议的那样,我宁愿避免忙碌等待。
答案 0 :(得分:117)
在.NET 4 SpinWait
中,在屈服之前执行10次迭代的CPU密集型旋转。但是在每个周期之后它不会立即返回到调用者 ;相反,它调用Thread.SpinWait
通过CLR(本质上是OS)旋转一段时间。该时间段最初为几十纳秒,但每次迭代加倍,直到10次迭代完成。这使得旋转(CPU密集型)阶段所花费的总时间的清晰度/可预测性成为可能,系统可以根据条件(核心数等)进行调整。如果SpinWait
在旋转产生阶段保持太长时间,它将定期休眠以允许其他线程继续(有关更多信息,请参阅J. Albahari's blog)。保证这个过程保持核心忙......
因此,SpinWait
将CPU密集型旋转限制为设定的迭代次数,然后在每次旋转时产生时间片(通过实际调用Thread.Yield
和Thread.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);
}
}
}