C++ 11 std :: condition_variable:我们可以直接将锁传递给通知的线程吗?

Quu*_*one 14 c++ concurrency multithreading condition-variable c++11

我正在学习C++ 11并发性,其中我以前唯一的并发原语经验是六年前的操作系统类,所以如果可以,请保持温和.

在C++ 11中,我们可以编写

std::mutex m;
std::condition_variable cv;
std::queue<int> q;

void producer_thread() {
    std::unique_lock<std::mutex> lock(m);
    q.push(42);
    cv.notify_one();
}

void consumer_thread() {
    std::unique_lock<std::mutex> lock(m);
    while (q.empty()) {
        cv.wait(lock);
    }
    q.pop();
}
Run Code Online (Sandbox Code Playgroud)

这样可以正常工作,但我觉得需要包裹cv.wait一个循环.我们需要循环的原因对我来说很清楚:

Consumer (inside wait())       Producer            Vulture

release the lock
sleep until notified
                               acquire the lock
                               I MADE YOU A COOKIE
                               notify Consumer
                               release the lock
                                                   acquire the lock
                                                   NOM NOM NOM
                                                   release the lock
acquire the lock
return from wait()
HEY WHERE'S MY COOKIE                              I EATED IT
Run Code Online (Sandbox Code Playgroud)

现在,我相信其中一个很酷的事情unique_lock就是我们可以传递它,对吧?如果我们能做到这一点,那将是非常优雅的:

Consumer (inside wait())       Producer

release the lock
sleep until notified
                               acquire the lock
                               I MADE YOU A COOKIE
                               notify and yield(passing the lock)
wake(receiving the lock)
return from wait()
YUM
release the lock
Run Code Online (Sandbox Code Playgroud)

现在有没有办法让秃鹫线程趁虚而入,因为互斥保持从锁定一路I MADE YOU A COOKIEYUM.另外,如果notify()要求您传递锁定,那么这是确保人们在调用之前实际锁定互斥锁的好方法notify()(请参阅信号传递条件变量(pthreads)).

我很确定C++ 11没有这个习惯用法的任何标准实现.这是什么历史原因(只是pthreads没有这样做?然后为什么会这样)?是否存在技术上的原因,冒险的C++编码器无法在标准C++ 11中实现这个习惯用法,或许可以调用它my_better_condition_variable

我也有一种模糊的感觉,也许我正在重新发明信号量,但我不记得从学校知道这是否准确.

How*_*ant 10

最终的答案是因为pthreads没有这样做.C++是一种封装操作系统功能的语言.C++不是操作系统或平台.因此它封装了Linux,unix和windows等操作系统的现有功能.

然而,pthreads也有很好的理由来解释这种行为.来自Open Group Base规格:

结果是,由于对pthread_cond_signal()的一次调用,多个线程可以从其对pthread_cond_wait()或pthread_cond_timedwait()的调用返回.这种效应称为"虚假唤醒".注意,情况是自我纠正的,因为被唤醒的线程数是有限的; 例如,在块上方的事件序列之后调用pthread_cond_wait()的下一个线程.

虽然可以解决这个问题,但是很少发生的边缘条件的效率损失是不可接受的,特别是考虑到必须检查与条件变量相关联的谓词.纠正此问题会不必要地降低此基本构建块中所有更高级别同步操作的并发度.

允许虚假唤醒的另一个好处是应用程序被迫在条件等待周围编写谓词测试循环.这也使得应用程序可以容忍在应用程序的某些其他部分中编码的相同条件变量上的多余条件广播或信号.由此产生的应用程序更加健壮.因此,IEEE Std 1003.1-2001明确记录了可能发生虚假唤醒的情况.

所以基本上声称你可以很容易地my_better_condition_variable在pthreads条件变量(或std::condition_variable)之上构建,而不会造成性能损失.但是,如果我们放在my_better_condition_variable基准级别,那么那些不需要功能的客户my_better_condition_variable无论如何都必须付费.

这种将最快,最原始的设计放在堆栈底部的理念,其目的是在它们之上构建更好/更慢的东西,在整个C++库中运行.在C++ lib无法遵循这一理念的地方,客户经常(并且正确地)烦恼.


R. *_*des 6

如果您不想编写循环,则可以使用带谓词的重载:

cv.wait(lock, [&q]{ return !q.is_empty(); });
Run Code Online (Sandbox Code Playgroud)

它定义为等效于循环,因此它与原始代码一样.

  • @Quuxplusone:"等效于"与C++标准中的"以实现方式"相去甚远."等效于"解释了预期的语义/结果,但这并不意味着这里有任何文字轮询循环. (2认同)