(1)條件變量std::condition_variable、wait() 、notify_one()、notify_all()
wait()用來等一個東西
//如果第二個參數返回值是true,那麼這一行就繼續往下運行。
//如果第二個參數返回值是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()