while(true)与wait+condition同步的区别

4

将线程放在while(true)循环中并测试条件是否满足以开始处理是一种不好的做法吗?

void run()
{
    for(;;)
    {
        if(dataReady)
        {
            processData();
        }
    }
}

使用等待/条件机制是否更可取:
void run()
{
    for(;;)
    {
        if(dataReady)
        {
            processData();
        }
        lock_guard lock(mutex);
        condition_.wait(lock);
    }
}

另一个线程当然会调用condition_.notify_one()

编辑:

我希望几乎永远不必等待。


1
请记住,除非dataReady是某种原子或受锁保护的数据类型,否则它将会导致数据竞争。(例如,如果它是一个普通的bool类型。) - GManNickG
我期望几乎不需要等待。这可能有点误导人。假设你只需要等待一次在10,000,000次操作中。当你的CPU运行在4GHz时,这仍然可能是相当多的等待。 - Martin York
6个回答

8

使用 while true 的方式是不好的,因为它只会消耗处理器的周期。
第二种方法更好,只有当线程需要执行某些工作时才通知它。


4
通常情况下,我会这么说。有时候自旋锁是合适的,但是那些在这种情况下工作的人通常不需要问是否适用。 :) - GManNickG
有所暗示?它有亲密关系吗? - Puppy
@DeadMG:Intimate 定义:宣布;声明;发布;传达;使人知晓。 - Alok Save
退出到操作系统并进行上下文切换比几个繁忙等待循环要昂贵得多。@GMan说,知道如何使用自旋锁的人在SO上不会问太多问题。如果您有足够的核心,自旋锁是良好的设计,因为自旋锁可以改善延迟。 - bestsss

5

这取决于您期望等待的时间长度。

对于非常短暂的等待时间,忙等待可能更可取,因为它不涉及上下文切换。有时上下文切换的开销可能超过整个忙等待循环的负担。


2
如果您采用第一种方法,您需要确保编译器从内存中实际读取变量,并且不会将从内存中读取的值优化为不能在循环内更改的值。将变量声明为“volatile”是必要的。
但仅此还不足够。您需要某种形式的内存屏障,以确保一个线程对变量的更改对另一个线程可见,并且存储和读取不会被CPU和缓存重新排序。如果这是在x86上,您可能可以逃脱。但如果您想做这种事情,最好使用编译器内置函数(例如Windows上的InterlockedIncrement或其他平台上的类似函数)。
对于几乎所有情况,最好使用条件变量或来自库的自旋锁(本质上就是您要实现的东西),因为它们将为多核处理获取正确的细节。

“volatile” 在实际解决方案中不需要存在。 - GManNickG

0

通常建议采用后者。但是这是任何线程或并发论文中的基本问题...

在旧处理器中,只有一个线程,并且无论处理器正在执行什么操作都会消耗电力,这是等待事物的常见习语。现在处理器有多个可以前进的线程,并且它们足够智能,如果您只是在等待条件,则不会花费功率。


0

前者通常是一个糟糕的想法,因为你会在一个核心上使用100%的CPU,什么也没做。你会消耗掉本可以被其他线程使用的资源(也许是设置 dataready 的线程)。

在第二个例子中,线程被暂停,直到收到通知。所以它不会占用CPU时间。


2
你所说的在很多情况下是正确的,但并非总是如此。如果你几乎从不需要等待,那么系统调用的开销可能会使用更多的 CPU 时间。此外,有时你的优先级是低延迟而不是低 CPU 使用率,在这种情况下,尽管使用了 100% 的 CPU,自旋锁可能仍然是一个好主意... 这取决于应用程序。 - jcoder
@JohnB:注意我说的是“通常”。同时请注意,我们在回答一个“不知道区别”的人,并且很可能不需要了解极其罕见的轮询更为优越的极端情况。是的,你是正确的,但我并没有真正看到这对OP有所帮助。 - jalf
确实,这都是公正的评论。 - jcoder
好的,实际上我不知道它是否无用。我的延迟限制以微秒为单位测量,但更低的延迟是更好的。 - Guillaume Paris
再次强调,规则应该是这样的:如果您关心性能和延迟,请同时实现两者,并测量哪个表现最佳。如果您不关心,请使用锁定。永远不要在没有测量它是否是更好的选择的情况下使用自旋锁。 - jalf
显示剩余3条评论

0

是的,这是一种不好的做法。忙等待循环只有在没有可用的锁和线程等复杂结构的地方才是合理的设计选择。

虽然,如果您可以保证您的软件是设备上唯一运行的应用程序(在嵌入式项目中可能是这种情况),您可能会采用忙等待循环。

但总的来说,请避免使用它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接