我是否需要同步std :: condition_variable/condition_variable_any :: notify_one

qbl*_*ble 15 c++ multithreading condition-variable thread-safety c++11

我需要同步std::condition_variable/condition_variable_any::notify_one吗?

据我所知,如果通知丢失是可以接受的 - 可以调用notify_one不受保护(例如通过互斥).

例如,我看到了以下使用模式(抱歉,不记得在哪里):

{
    {
        lock_guard<mutex> l(m);
        // do work
    }
    c.notify_one();
}
Run Code Online (Sandbox Code Playgroud)

但是,我检查了libstdc ++来源,我看到:

condition_variable :: notify_one

void condition_variable::notify_one() noexcept
{
    int __e = __gthread_cond_signal(&_M_cond);
    // XXX not in spec
    // EINVAL
    if (__e)
        __throw_system_error(__e);
}
Run Code Online (Sandbox Code Playgroud)

condition_variable_any :: notify_one:

void condition_variable_any::notify_one() noexcept
{
    lock_guard<mutex> __lock(_M_mutex);
    _M_cond.notify_one();
}
Run Code Online (Sandbox Code Playgroud)

这里是condition_variable_any的布局:

class condition_variable_any
{
    condition_variable _M_cond;
    mutex _M_mutex;
    // data end
Run Code Online (Sandbox Code Playgroud)

即它只是condition_variable + mutex周围的薄包装.

所以,问题:

  1. 它是线程安全的,不保护notify_one的互斥体的任一condition_variable_anycondition_variable
  2. 为什么condition_variable_any的实现使用额外的互斥锁?
  3. 为什么实施condition_variable_any::notify_onecondition_variable::notify_one不同?也许condition_variable::notify_one需要手动保护,但condition_variable_any::notify_one不是吗?它是libstdc ++ bug吗?

Jon*_*ely 14

即它只是condition_variable + mutex周围的薄包装.

呃没有.仅仅因为它具有这些类型的成员并不能使它成为一个薄的包装器.试着了解它的实际功能,而不仅仅是其私有成员的类型.那里有一些非常微妙的代码.

  1. 对于condition_variable_any或condition_variable,不通过互斥锁保护notify_one是不是线程安全的?

是.

实际上,notify_one()使用锁定互斥锁进行调用将导致等待线程被唤醒,尝试锁定互斥锁,发现它仍然被通知线程锁定,然后返回休眠状态直到释放互斥锁.

如果在notify_one()没有锁定互斥锁的情况下调用,则唤醒线程可以立即运行.

2为什么condition_variable_any的实现使用额外的互斥锁?

condition_variable_any可以与任何Lockable类型一起使用,不仅仅是std:mutex,但在libstdc ++中的内部使用a condition_variable,它只能用于std::mutex,因此它也有一个内部std::mutex对象.

因此,condition_variable_any使用两个互斥锁工作,用户提供的外部互斥和实现使用的内部互斥.

3为什么condition_variable_any :: notify_one和condition_variable :: notify_one的实现不同?也许condition_variable :: notify_one需要手动保护但是condition_variable_any :: notify_one不需要?它是libstdc ++ bug吗?

不,这不是一个错误.

该标准要求呼叫wait(mx)必须原子解锁mx和睡眠.libstdc ++使用内部互斥锁来提供原子性保证.必须锁定内部互斥锁,以避免在其他线程即将等待的情况下错过通知condition_variable_any.