C# SpinWait 与睡眠等待。使用哪一种?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9719003/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
SpinWait vs Sleep waiting. Which one to use?
提问by Anastasiosyal
Is it efficient to
是否有效
SpinWait.SpinUntil(() => myPredicate(), 10000)
for a timeout of 10000ms
超时 10000 毫秒
or
或者
Is it more efficient to use Thread.Sleeppolling for the same condition
For example something along the lines of the following SleepWaitfunction:
Thread.Sleep对相同条件使用轮询是否更有效例如,类似于以下SleepWait函数的内容:
public bool SleepWait(int timeOut)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
while (!myPredicate() && stopwatch.ElapsedMilliseconds < timeOut)
{
Thread.Sleep(50)
}
return myPredicate()
}
I'm concerned that all the yielding of SpinWait may not be a good usage pattern if we are talking about timeouts over 1sec? Is this a valid assumption?
我担心如果我们谈论超过 1 秒的超时,所有 SpinWait 的产生可能不是一个好的使用模式?这是一个有效的假设吗?
Which approach do you prefer and why? Is there another even better approach?
您更喜欢哪种方法,为什么?还有另一种更好的方法吗?
Update- Becoming more specific:
更新- 变得更具体:
Is there a way to Make BlockingCollection Pulse a sleeping thread when it reaches bounded capacity? I rather avoid a busy waits alltogether as Marc Gravel suggests.
有没有办法在达到有界容量时使 BlockingCollection Pulse 成为休眠线程?我宁愿避免像 Marc Gravel 建议的那样忙于等待。
采纳答案by Marc Gravell
The bestapproach is to have some mechanism to actively detectthe thing becoming true (rather than passively polling for it having becometrue); this could be any kind of wait-handle, or maybe a Taskwith Wait, or maybe an eventthat you can subscribe to to unstick yourself. Of course, if you do that kind of "wait until something happens", that is stillnot as efficient as simply having the next bit of work done as a callback, meaning: you don't need to use a thread to wait. Taskhas ContinueWithfor this, or you can just do the work in an eventwhen it gets fired. The eventis probably the simplest approach, depending on the context. Task, however, already provides most-everything you are talking about here, including both "wait with timeout" and "callback" mechanisms.
在最好的办法是有一些机制来主动检测的东西变成真正的(而不是被动地轮询它已经成为真正的); 这可能是任何类型的等待句柄,或者可能是Taskwith Wait,或者可能event是您可以订阅以取消自己的。当然,如果你做那种“等到事情发生”的方式,那仍然不如简单地将下一个工作作为回调完成,这意味着:你不需要使用线程来等待。Task有ContinueWith这个,或者你可以event在它被解雇时做这项工作。这event可能是最简单的方法,具体取决于上下文。Task,但是,已经提供了您在这里谈论的大部分内容,包括“超时等待”和“回调”机制。
And yes, spinning for 10 seconds is not great. If you want to use something like your current code, and if you have reason to expect a short delay, but need to allow for a longer one - maybe SpinWaitfor (say) 20ms, and use Sleepfor the rest?
是的,旋转 10 秒并不好。如果您想使用类似于您当前代码的内容,并且您有理由期待一个短暂的延迟,但需要允许更长的延迟 - 可能SpinWait(例如)20 毫秒,然后Sleep用于其余时间?
Re the comment; here's how I'd hook an "is it full" mechanism:
重新评论;这是我如何挂钩“是否已满”机制:
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);
}
}
with, in the "put back into the collection" code:
在“放回集合中”代码中:
if(CollectionIsFull) {
lock(syncLock) {
if(CollectionIsFull) { // double-check with the lock
Monitor.PulseAll(syncLock);
}
}
}
回答by MoonKnight
In .NET 4 SpinWaitperforms CPU-intensive spinning for 10 iterations before yielding. But it does not return to the caller immediatelyafter each of those cycles; instead, it calls Thread.SpinWaitto spin via the CLR (essentially the OS) for a set time period. This time period is initially a few tens of nano-seconds but doubles with each iteration until the 10 iterations are complete. This enables clarity/predictability in the total time spent spinning (CPU-intensive) phase, which the system can tune according to conditions (number of cores etc.). If SpinWaitremains in the spin-yielding phase for too long it will periodically sleep to allow other threads to proceed (see J. Albahari's blogfor more information). This process is guaranteed to keep a core busy...
在 .NET 4 中SpinWait,在产生之前执行 10 次迭代的 CPU 密集型旋转。但它不会在每个循环后立即返回给调用者;相反,它会Thread.SpinWait在设定的时间段内通过 CLR(本质上是操作系统)调用自旋。这个时间段最初是几十纳秒,但每次迭代都会加倍,直到 10 次迭代完成。这使得在旋转(CPU 密集型)阶段所花费的总时间变得清晰/可预测,系统可以根据条件(内核数量等)进行调整。如果SpinWait在自旋产生阶段停留太久,它将定期休眠以允许其他线程继续进行(有关更多信息,请参阅J. Albahari 的博客)。这个过程保证让核心保持忙碌......
So, SpinWaitlimits the CPU-intensive spinning to a set number of iterations, after which it yields its time slice on every spin (by actually calling Thread.Yieldand Thread.Sleep), lowering its resource consumption. It will also detect if the user is running a single core machine and yield on every cycle if that is the case.
因此,SpinWait将 CPU 密集型旋转限制为设定的迭代次数,之后它会在每次旋转时产生其时间片(通过实际调用Thread.Yield和Thread.Sleep),从而降低其资源消耗。它还将检测用户是否正在运行单核机器,如果是这种情况,则在每个周期都产生收益。
With Thread.Sleepthe thread is blocked. But this process will not be as expensive as the above in terms of CPU.
随着Thread.Sleep线程被阻塞。但是这个过程在CPU方面不会像上面那样昂贵。

