使用的std ::互斥的std :: condition_variable和std :: uniqu

2019-07-03 19:46发布

我遇到一些无法理解的条件变量及其与互斥体的使用,我希望社会能够帮助我。 请注意,我来自一个Win32的背景,所以我与CRITICAL_SECTION使用,手柄,SetEvent的,WaitForMultipleObject等。

下面是在并发使用C ++ 11标准库中我第一次尝试,这是一个修改后的版本在这里找到程序示例 。

#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;
}

这个几个问题。

我读过 “称,拟向等待STD任何线程:: condition_variable必须首先获得一个std :: unique_lock。”

所以我有一个{退出互斥,条件变量与布尔}指示何时戒烟已经发出信号。 生产者和消费者线程必须在每次获取的std :: unique_lock像这样:

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

这是混淆了我的地狱。 这会不会锁定在第一个线程退出互斥体,从而阻断第二? 如果这是真的,那么如何在第一个线程释放锁,以便其他线程可以开始?

另一个问题是:如果我改变wait_for()调用等待零秒,该线程饿死。 有人能解释一下吗? 我希望它不会执行while循环之前阻止(我是正确的假设,一个是了no_timeout的recv'd代替超时?)。

我怎么能叫一个wait_for(),并指定一个零时间,使wait_for()调用不会阻塞,而是只检查病情,并继续?

我也很想听到关于这个问题的很好的参考。

Answer 1:

这会不会锁定在第一个线程退出互斥体,从而阻断第二?

是。

如果这是真的,那么如何在第一个线程释放锁,以便其他线程可以开始?

当你等待一个condition_variable它解锁,你传递给它的锁,所以在

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

条件变量会调用lock.unlock()然后封锁长达10ms的(这种情况发生原子所以有解锁的互斥体和阻挡之间没有窗口,在那里的条件可能成为准备就绪,你会错过它)

当互斥量被释放它允许其他线程获得它的锁。

另一个问题是:如果我改变wait_for()调用等待零秒,该线程饿死。 有人能解释一下吗?

我希望其他的线程被饿死的,因为互斥不会被解锁足够长,使其他线程锁定。

我是正确的假设,一个是了no_timeout的recv'd代替超时?

不,如果持续时间过去而没有条件甚至是零秒后变为就绪则“超时”。

我怎么能叫一个wait_for(),并指定一个零时间,使wait_for()调用不会阻塞,而是只检查病情,并继续?

不要使用条件变量! 如果你不想等待条件要成为真正的,不要等到条件变量! 只是测试m_bQuit和继续。 (题外话,为什么你叫布尔m_bXxx ?他们不是会员,所以m_前缀是误导性的,而b前缀看起来像匈牙利表示法......这臭的那可怕的MS习惯。)

我也很想听到关于这个问题的很好的参考。

最好是参考安东尼威廉姆斯的C ++并发在行动 ,其覆盖详细整个C ++ 11原子学和线程库,以及多线程程序设计的一般原则。 一个关于这个问题我最喜欢的书是Butenhof的编程与POSIX线程 ,这是专门针对P线程,但C ++ 11的设施非常密切映射到P线程,所以很容易将信息从该书转移到C ++ 11多线程。

NB在thrQuit你写m_bQuit没有用互斥锁保护它,因为没有什么可以阻止另一个线程在同一时间为写读它,这是一个竞争条件,即不确定的行为。 到布尔写入必须通过一个互斥被保护或必须的原子类型,例如std::atomic<bool>

我不认为你需要两个互斥体,它只是增加了竞争。 因为你永远不放开mtxQuit除,而在等待condition_variable存在具有第二互斥任何时候都未将mtxQuit一个已经确保只有一个线程可以同时进入临界区。



Answer 2:

如果您想查询的东西,并继续不论其真实与否(可能是做两件不同的事情),那么条件变量是使用错误的事情。 条件变量是与要等待,而不必以旋转获取和释放锁锁定数据结构相关联的一些条件低电平原语。 典型的例子是一个队列 - 你有一个锁守卫进入队列和两个条件瓦尔(队列不空,排队未满)。 为推动东西在队列中,你获得锁,检查其不充分,在没有满condvar等待,如果是这样,在队列推值,信号的不空condvar(因为它不再是空的)和解除锁定。 弹出操作是类似的。

所以你的情况,你有一个简单的队列不能充满,所以你需要一个锁,它一个condvar。 非常有意义。 但你必须要具有触发完成一个“退出”标志。 你不想等待退出标志被设置 - 你想真正做工作,直到它被设置 - 这样一个condvar真的是没有意义在这里。 是的,你能想出一个令人费解的安排,将使其工作,但由于其没有使用条件变量的条件变量,这将是混乱的。

它更有意义(和更清晰),只使用一个std::atomic<bool>的退出标志。 然后你只需把它初始化为假,在你退出线程设置为true,并检查它在其他线程。



文章来源: Using std::mutex, std::condition_variable and std::unique_lock