c++ - 使用 std::mutex、std::condition_variable 和 std::unique_lock

标签 c++ multithreading concurrency mutex condition-variable

我在理解条件变量及其在互斥锁中的使用方面遇到了一些问题,希望社区可以帮助我。请注意,我来自 win32 背景,所以我与 CRITICAL_SECTION、HANDLE、SetEvent、WaitForMultipleObject 等一起使用。

这是我第一次尝试使用 c++11 标准库进行并发,它是 program example found here 的修改版本.

#include <condition_variable>
#include <mutex>
#include <algorithm>
#include <thread>
#include <queue>
#include <chrono>
#include <iostream>


int _tmain(int argc, _TCHAR* argv[])
{   
    std::queue<unsigned int>    nNumbers;

    std::mutex                  mtxQueue;
    std::condition_variable     cvQueue;
    bool                        m_bQueueLocked = false;

    std::mutex                  mtxQuit;
    std::condition_variable     cvQuit;
    bool                        m_bQuit = false;


    std::thread thrQuit(
        [&]()
        {
            using namespace std;            

            this_thread::sleep_for(chrono::seconds(7));

            // set event by setting the bool variable to true
            // then notifying via the condition variable
            m_bQuit = true;
            cvQuit.notify_all();
        }
    );

    std::thread thrProducer(
        [&]()
        {           
            using namespace std;

            int nNum = 0;
            unique_lock<mutex> lock( mtxQuit );

            while( ( ! m_bQuit ) && 
                   ( cvQuit.wait_for( lock, chrono::milliseconds(10) ) == cv_status::timeout ) )
            {
                nNum ++;

                unique_lock<mutex> qLock(mtxQueue);
                cout << "Produced: " << nNum << "\n";
                nNumbers.push( nNum );              
            }
        }
    );

    std::thread thrConsumer(
        [&]()
        {
            using namespace std;            

            unique_lock<mutex> lock( mtxQuit );

            while( ( ! m_bQuit ) && 
                    ( cvQuit.wait_for( lock, chrono::milliseconds(10) ) == cv_status::timeout ) )
            {
                unique_lock<mutex> qLock(mtxQueue);
                if( nNumbers.size() > 0 )
                {
                    cout << "Consumed: " << nNumbers.front() << "\n";
                    nNumbers.pop();
                }               
            }
        }
    );

    thrQuit.join();
    thrProducer.join();
    thrConsumer.join();

    return 0;
}

关于这个的几个问题。

I've read that “任何打算在 std::condition_variable 上等待的线程必须首先获得一个 std::unique_lock。”

所以我有一个 {quit mutex, condition variable & bool} 来指示何时发出退出信号。生产者和消费者线程必须各自获得一个 std::unique_lock :

std::unique_lock<std::mutex> lock(m_mtxQuit);

这让我很困惑。这不会将退出互斥锁锁定在第一个线程中,从而阻塞第二个线程吗?如果这是真的,那么第一个线程如何释放锁,以便另一个线程可以开始?

另一个问题:如果我将 wait_for() 调用更改为等待零秒,则该线程将被饿死。有人可以解释吗?我希望它在执行 while 循环之前不会阻塞(我是否正确假设 no_timeout 是recv'd 而不是超时?)。

如何调用 wait_for() 并指定零时间,以便 wait_for() 调用不会阻塞,而只是检查条件并继续?

我也很想听听关于这个主题的好的引用资料。

最佳答案

Won't this lock the quit mutex in the first thread, thereby blocking the second?

是的。

And if that's true, then how does the first thread release the lock, so that the other thread can begin?

当您等待 condition_variable它会解锁你通过它的锁,所以在

cvQuit.wait_for( lock, chrono::milliseconds(10) )

条件变量将调用 lock.unlock()然后阻塞长达 10 毫秒(这是原子发生的,因此在解锁互斥锁和阻塞条件可能准备好的地方之间没有窗口,你会错过它)

当互斥锁被解锁时,它允许其他线程获取它的锁。

Another question: If I change the wait_for() call to wait for zero seconds, that thread is starved. Can someone explain?

我预计 other 线程会被饿死,因为互斥锁的解锁时间不足以让其他线程锁定它。

am I correct to assume that a no_timeout is recv'd instead of a timeout?

不,如果持续时间过去了条件还没有准备好,那么即使在零秒之后它也会“超时”。

How can I call a wait_for() and specify a zero time, so that the wait_for() call doesn't block, instead it just checks the condition and continues?

不要使用条件变量!如果您不想等待条件变为真,请不要等待条件变量!只需测试m_bQuit并继续。 (除此之外,为什么你的 bool 值被称为 m_bXxx ?它们不是成员,所以 m_ 前缀具有误导性,而 b 前缀看起来就像 MS 匈牙利表示法的糟糕习惯......这很臭。)

I'd also be interested to hear about good references on this subject.

最好的引用是 Anthony Williams 的 C++ Concurrency In Action其中详细介绍了整个 C++11 原子和线程库,以及多线程编程的一般原则。我最喜欢的关于这个主题的书之一是 Butenhof 的 Programming with POSIX Threads。 ,这是特定于 Pthreads 的,但 C++11 工具与 Pthreads 的映射非常密切,因此很容易将信息从那本书转移到 C++11 多线程。

注意在 thrQuit你写信给 m_bQuit没有用互斥锁保护它,因为没有什么能阻止另一个线程在写入的同时读取它,这是一个竞争条件,即未定义的行为。对 bool 的写入必须受互斥体保护,或者必须是原子类型,例如std::atomic<bool>

我认为您不需要两个互斥锁,它只会增加争用。由于您从未发布 mtxQuit除了等待 condition_variable没有第二个互斥体 mtxQuit 是没有意义的。一个已经确保只有一个线程可以一次进入临界区。

关于c++ - 使用 std::mutex、std::condition_variable 和 std::unique_lock,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13371020/

相关文章:

在本地资源上同步期间的 Java 死锁?

concurrency - 为什么有并发的矩阵乘法比没有并发的慢?

c++ - 未声明的标识符/数组

c++ - 用 spirit::qi 检查双重符号

javascript - Angular - 同时执行多个功能

Java 使列表线程的副本安全吗?

node.js - 当通过来自 NodeJS 服务器的单个连接使用 Redis 时,我可以做哪些假设

c++ - 我的 AQTime 结果中的时间总和与我测量的响应时间不匹配

c++ - 在 OpenCV 中指定算术运算的输出类型

C#,有没有 "thread-safe"流这样的东西?