(1)條件變量std::condition_variable、wait() 、notify_one()、notify_all()
wait()用來等一個東西
1、如果第二個參數傳回值是true,那麼這一行就繼續往下運作。
2、如果第二個參數傳回值是false那麼wait将解鎖互斥量,并堵塞在這一行
堵到什麼時候呢?堵到其他函數調用notify_one()函數為止。
如果wait沒有第二個參數,那麼第二個參數傳回false效果一樣。
當其他函數使用notify_one将wait喚醒,然後wait就會重新嘗試擷取鎖。如果擷取不到,代碼就卡在wait這邊擷取鎖,如果擷取到鎖,就會繼續執行。
1)喚醒wait後,并加鎖成功接下來:
如果第二個參數傳回值是true,那麼這一行就繼續往下運作。
如果第二個參數傳回值是false那麼wait将解鎖互斥量,并堵塞在這一行,等待喚醒
2)如果wait沒有第二個參數,隻要被喚醒,那麼一旦擷取到鎖,就從wait這邊繼續向下執行。
(2)上述代碼的深入思考
(3) notify_all()
條件變量std::condition_variable wait() notify_one()
class A
{
public:
//把收到的消息放到一個隊列中
void inMsgRecvQueue() //unlock()
{
for(int i=0;i<10000;++i)
{
cout<<"inMsgRecvQueue():"<<i<<endl;
std::unique_lock<std::mutex> lguard1(my_mutex1);
msgRecvQueue.push_back(i);//假設這個數字就是收到的指令,把他加入到隊列中
//代碼處理。。。
}
return ;
}
bool outMsgProc(int &command)
{ //雙重鎖定,雙重檢查
if(!msgRecvQueue.empty())
{ //添加這個雙重鎖定是因為每次不管是不是空都加鎖然後再釋放效率低,這樣可以避免每次都加鎖
std::unique_lock<std::mutex> lguard1(my_mutex1);
if(!msgRecvQueue.empty())
{
command = msgRecvQueue.front();//傳回第一個元素,但不檢查元素是否存在
msgRecvQueue.pop_front();//移除第一個元素
return true;
}
}
return false;
}
//把資料從消息隊列中取出線程
void outMsgRecvQueue()
{
int command =0;
for(int i=0;i<10000;++i)
{
bool result=outMsgProc(command);
if(result==true)
{
cout<<"outMsgRecvQueue()執行,取出一個元素"<<command<<endl;
//對消息處理
}
else
{
//消息隊列為空
sleep(100);
cout<<"outMsgRecvQueue()執行,但是消息隊列為空"<<i<<endl;
}
}
cout<<"end"<<endl;
}
private:
std::list<int> msgRecvQueue;//容器(消息隊列)專門用于代表玩家發送的指令
std::mutex my_mutex1;//建立了一個互斥量
std::mutex my_mutex2;//建立了一個互斥量
};
int main()
{ //條件變量std::condition_variable wait() notify_one()
//線程A:等待一個條件滿足
//線程B:向消息隊列中放消息
A myobja;
std::thread my_out_Thread(&A::outMsgRecvQueue,&myobja);//outMsgRecvQueue為起始函數的線程
std::thread my_in_Thread(&A::inMsgRecvQueue,&myobja);//inMsgRecvQueue為起始函數的線程
outMsgRecvQueue.join();
inMsgRecvQueue.join();
cout<<"主線程收尾,正常退出"<endl;
return 0;
}
------------------------------------------------
class A
{
public:
//把收到的消息放到一個隊列中
void inMsgRecvQueue() //unlock()
{
for(int i=0;i<10000;++i)
{
std::unique_lock<std::mutex> lguard1(my_mutex1);
cout<<"inMsgRecvQueue()插入一個元素:"<<i<<endl;
msgRecvQueue.push_back(i);//假設這個數字就是收到的指令,把他加入到隊列中
/*
my_con.notify_one();//我們嘗試把wait()喚醒,這行運作完,那麼outMsgRecvQueue中的wait就會被喚醒
notify_one()通知喚醒一個線程,隻能通知一個線程。
*/
my_con.notify_all();//喚醒所有處于wait狀态的線程
//代碼處理。。。
}
return;
}
//把資料從消息隊列中取出線程
void outMsgRecvQueue()
{
int command =0;
while(true)
{
std::unique_lock<std::mutex> lguard1(my_mutex1);
//wait()用來等一個東西
//如果第二個參數傳回值是true,那麼這一行就繼續往下運作。
//如果第二個參數傳回值是false那麼wait将解鎖互斥量,并堵塞在這一行
//堵到什麼時候呢?堵到其他函數調用notify_one()函數為止
//如果wait沒有第二個參數,那麼第二個參數傳回false效果一樣。
當其他函數使用notify_one将wait喚醒,然後wait就會重新嘗試擷取鎖。
如果擷取不到,代碼就卡在wait這邊擷取鎖,如果擷取到鎖,就會繼續執行。
1)喚醒wait後,并加鎖成功接下來:
如果第二個參數傳回值是true,那麼這一行就繼續往下運作。
如果第二個參數傳回值是false那麼wait将解鎖互斥量,并堵塞在這一行,等待喚醒
2)如果wait沒有第二個參數,隻要被喚醒,那麼一旦擷取到鎖,就從wait這邊繼續向下執行。
my_con.wait(lguard1,[this]{ //一個lambda表達式,就是一個可調用對象
if(!msgRecvQueue.empty())
return true;
return false;
});
command = msgRecvQueue.front();//傳回第一個元素,但不檢查元素是否存在
msgRecvQueue.pop_front();//移除第一個元素
cout<<"outMsgRecvQueue取出一個元素"<<std::this_thread::get_id()<<endl;
lguard1.unlcok(); //因為unique_lock的靈活性,是以可以随時解鎖、免得鎖太久
}
cout<<"end"<<endl;
}
private:
std::list<int> msgRecvQueue;//容器(消息隊列)專門用于代表玩家發送的指令
std::mutex my_mutex1;//建立一個互斥量
std::condition_variable my_con;//建立一個條件變量對象
};
int main()
{ //條件變量std::condition_variable wait() notify_one()
//線程A:等待一個條件滿足
//線程B:向消息隊列中放消息
std::condition_variable實際上是一個類,是一個和條件相關的一個類。
需要等待一個條件達成,這個類需要和互斥量配合使用,需要生成一個類的對象
A myobja;
std::thread my_out_Thread(&A::outMsgRecvQueue,&myobja);//outMsgRecvQueue為起始函數的線程
std::thread my_out_Thread2(&A::outMsgRecvQueue,&myobja);
std::thread my_in_Thread(&A::inMsgRecvQueue,&myobja);//inMsgRecvQueue為起始函數的線程
outMsgRecvQueue.join();
outMsgRecvQueue2.join();
inMsgRecvQueue.join();
cout<<"主線程收尾,正常退出"<endl;
return 0;
}
二、notify_all()