为啥 Boost 库在其线程屏障的实现中使用 m_ge​​neration 变量?

Posted

技术标签:

【中文标题】为啥 Boost 库在其线程屏障的实现中使用 m_ge​​neration 变量?【英文标题】:Why does the Boost library use an m_generation variable in its implementation of a thread barrier?为什么 Boost 库在其线程屏障的实现中使用 m_ge​​neration 变量? 【发布时间】:2014-11-29 05:09:18 【问题描述】:

boost 库(在 C++11 标准之前)提供了对线程的支持。作为其支持的一部分,它还提供了“屏障”的实现,这是一个允许同步的简单类。引用boost website:

“屏障是一个简单的概念。也称为集合点,它是多个线程之间的同步点。屏障被配置为特定数量的线程(n),当线程到达屏障时,它们必须等待直到所有n个线程都到达了。一旦第n个线程到达屏障,所有等待的线程都可以继续,屏障被重置。"

屏障(等待)的主要功能的实现,截至Boost 1.54,如下图所示:

bool wait()

    boost::mutex::scoped_lock lock(m_mutex);
    unsigned int gen = m_generation;

    if (--m_count == 0)
    
        m_generation++;
        m_count = m_threshold;
        m_cond.notify_all();
        return true;
    

    while (gen == m_generation)
        m_cond.wait(lock);
    return false;

可见屏障是可重复使用的:一旦构建,第一次使用后不需要销毁。

我现在的问题是:变量 m_ge​​neration 是干什么用的?我假设 boost 库的作者有理由包含它。每次重置/准备重用屏障时,它都会增加,但目的是什么?它是一个私有变量,因此无法从外部读取。同样的问题也可以通过 wait() 函数内部的一个简单的内部 bool 变量轻松解决,而无需私有类变量。

【问题讨论】:

【参考方案1】:

简而言之,需要m_generation来处理spurious wakeups。

生成计数器与条件变量一起使用,向所有在屏障上等待的线程发出信号,表明它们可以继续进行:

一旦有m_threshold 线程到达屏障,它的代号就会增加,并且条件变量会发出信号。这会导致等待线程(即那些较早到达屏障的线程)从m_cond.wait(lock) 唤醒。

现在,等待线程可以从m_cond.wait(lock) 唤醒,以获取other reasons。这就是m_generation 出现的地方:如果它被更改,那么屏障已经被重置并且线程可以继续。如果m_generation 仍然包含相同的值,则线程需要回到m_cond.wait(lock)

wait() 中使用自动变量无法实现这一点,因为每个线程都有自己的实例。

【讨论】:

啊!非常感谢。我忽略了一个事实,即内部变量会导致每个线程都有自己的实例。现在它是有道理的。非常感谢。

以上是关于为啥 Boost 库在其线程屏障的实现中使用 m_ge​​neration 变量?的主要内容,如果未能解决你的问题,请参考以下文章

在多线程应用程序中使用屏障的真实示例是啥?

使用互斥量和信号量的屏障实现

为啥需要内存屏障?

为啥标准 C# 事件调用模式是线程安全的,没有内存屏障或缓存失效?类似的代码呢?

为啥将先前杀死的 boost 线程的线程 id 分配给新的 boost 线程?

使用 Boost::thread 类的线程池实现