多线程编程环境下,可能存在这样一种情况:对于某个共享数据,例如一个全局的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是空的,导致程序崩溃。
一个可行的解决办法是使用条件变量,即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秒后执行了删除操作。