夯实基础(3):条件变量

243 阅读1分钟

多线程编程环境下,可能存在这样一种情况:对于某个共享数据,例如一个全局的vector对象,线程1为其添加元素而线程2删除其首元素,很明显,执行删除操作的前提是容器对象中有元素存在。

#include<vector>
#include<mutex>
std::vector<int>globalV;
std::mutex m_mutex;
void setGlobalV1(){
    std::unique_lock<std::mutex>lg(m_mutex);
    std::cout<<"1:"<<getTime()<<std::endl;
    globalV.push_back(6);
}

void setGlobalV2(){
    if(!globalV.empty()){
        std::unique_lock<std::mutex>lg(m_mutex);
        std::cout<<"2:"<<getTime()<<std::endl;
        globalV.erase(globalV.begin());
    }
}

//main()
while(true){
    std::thread t2(setGlobalV2);
    std::thread t1(setGlobalV1);
    t1.join();
    t2.join();
}

上述代码中,getTime()函数用来获取系统时间。由于线程执行乱序,有可能线程2先执行,但globalV是空的,导致程序崩溃。

thread.PNG
一个可行的解决办法是使用条件变量,即std::condition_variable类。当线程1往globalV中插入元素后,会通知线程2。

#include<vector>
#include<mutex>
std::vector<int>globalV;
std::mutex m_mutex;
std::condition_variable cv;
void setGlobalV1(){
    std::unique_lock<std::mutex>lg(m_mutex);
    std::cout<<"1:"<<getTime()<<std::endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    globalV.push_back(6);
    cv.notify_one();
}

void setGlobalV2(){
    if(!globalV.empty()){
        std::unique_lock<std::mutex>lg(m_mutex);
        cv.wait(lg,[]{
            if(globalV.empty())
                return false;
            return true;
        });
        std::cout<<"2:"<<getTime()<<std::endl;
        globalV.erase(globalV.begin());
    }
}

//main()
while(true){
    std::thread t2(setGlobalV2);
    std::thread t1(setGlobalV1);
    t1.join();
    t2.join();
}

上述代码中,wait()函数以及notify_one()函数的原型如下:

template<class _Predicate>
void wait(unique_lock<mutex>&_Lck,_Predicate _Pred)
{
    //wait for signal and test predicate
    while(!_Pred())
        wait(_Lck);
}
void notify_one() _NOEXCEPT
{
    //wake up one waiter
    _Cnd_signalX(_Mycnd());
}

线程2在要操作globalV时,会检查其是否为空(即wait函数中的第二个参数),如果是空的,则会阻塞在该处,同时解锁互斥量,直到线程1向globalV中成功添加一个元素后,使用notify_one()函数唤醒阻塞在wait函数的线程2。在线程1的入口函数setGlobalV1让该线程在插入元素前等待了5秒,根据输出可以发现,线程2在5秒后执行了删除操作。

thread.PNG