如果我Thread.Sleep(),它真的很忙吗?

uri*_*rig 11 multithreading thread-sleep

我的问题对定义有点挑剔:

下面的代码可以描述为"忙碌的等待"吗?尽管它使用Thread.Sleep()来允许上下文切换?

while (true) {
    if (work_is_ready){
        doWork();
    }
    Thread.Sleep(A_FEW_MILLISECONDS);
}
Run Code Online (Sandbox Code Playgroud)

PS - 维基百科中忙碌等待的当前定义表明,这是一种"少浪费"的忙碌等待形式.

Jim*_*hel 8

无论轮询操作之间的时间如何,任何轮询循环都是忙碌的等待.当然,睡眠几毫秒比没有睡眠要忙得多,但它仍然涉及处理:线程上下文切换和一些最小条件检查.

非忙等待是阻塞呼叫.示例的非繁忙版本将涉及等待同步原语,例如事件或条件变量.例如,这个伪代码:

// initialize an event to be set when work is ready
Event word_is_ready;
work_is_ready.Reset();

// in code that processes work items
while (true)
{
    work_is_ready.Wait();  // non-busy wait for work item
    do_work();
}
Run Code Online (Sandbox Code Playgroud)

这里的区别在于没有定期轮询.在Wait设置事件之前,永远不会调度调用块和线程.

  • c2.com(它似乎是授权软件设计网站)不同意你的看法.以下是他们的例子:`BusyWaiting:while(MyFileIsNotReady()){//什么都不做}`和轮询:`while(MyFileIsNotReady()){Sleep(1000); }`.我看到睡眠是唯一的区别. (2认同)

dca*_*tro 6

那不是在等待.忙碌的等待或旋转,恰恰相反:避免上下文切换.

如果你想允许其他线程运行,当且仅当其他线程准备运行时,以避免单线程CPU中的死锁情况(例如,当前线程需要work_is_ready设置为true,但如果此线程不提供处理器并让其他人运行,它永远不会被设置为true,你可以使用Thread.Sleep(0).

一个更好的选择是使用SpinWait.SpinUntil

SpinWait.SpinUntil(() => work_is_ready);
doWork();
Run Code Online (Sandbox Code Playgroud)

SpinWait发出特殊的rep; nop(重复无操作)或pause指令,让处理器知道您正忙着等待,并针对超线程CPU进行了优化.此外,在单核CPU中,这将yield立即处理器(因为如果只有一个核心,忙碌等待是完全无用的).


但是,旋转只有您完全确定不会等待条件的时间超过处理器将上下文切换回来并再次返回时才有用.即,不超过几微秒.

如果您想每隔几毫秒轮询一个条件,那么您应该使用阻塞同步原语,如维基页面所示.对于您的场景,我建议使用一个AutoResetEvent,在调用WaitOne事件之前阻塞线程,直到事件已经发出信号(即条件变为真).

另请参阅:同步基元概述

  • 我不认为在“几毫秒”的时间内旋转会更有效率。一次上下文切换需要内核大约 30 微秒;旋转会在整个等待时间(3 毫秒 == 30,000 微秒)内浪费所有内核的 CPU 周期。只有在旋转所花费的时间少于进行上下文切换所需的时间时,旋转才有意义。 (2认同)