C++11 conditon、wait、notify_one、notify_all
一、条件变量std::conditon_variable、wait()、notify_one()
1.1、std::conditon_variable以及wait()和notify_one()
#include <iostream>
#include <thread>
#include <list>
#include <mutex>
#include <chrono>
class A
{
public:
void inMsgRecvList()
{
for (int i = 0; i < 100000; ++i)
{
std::cout << "inMsgRecvList()执行插入一个元素" << i<<std::endl;
std::unique_lock<std::mutex> myUniqueLock(myMutex1);
msgRecvList.push_back(i);
myCondition.notify_one();
myUniqueLock.unlock();
}
return;
}
void outMsgRecvList()
{
int commond = 0;
while (true)
{
{
std::unique_lock<std::mutex> myUniqueLock(myMutex1);
//wait()等待一个条件
/*如果第二个参数(lambda表达式)的返回值是false,那么wait()将解锁互斥量,并阻塞到本行
阻塞到其他某个线程调用notify_one()成员函数为止
如果第二个参数的返回值是true,则wait立即返回
如果wait()没有第二个参数,则会阻塞到本行解锁互斥量,直到某个线程调用notify_one();
当其他线程调用notify_one,wait()被唤醒,wait()不断地尝试互斥锁,如果获取不到,就阻塞
如果wait()有第二个参数,就判断返回值,如果是false(),解锁互斥量。再次等待被唤醒
如果表达式为true这立即返回,程序继续执行
如果没有第二个参数,被唤醒就向下执行
*/
myCondition.wait(myUniqueLock , [this] {
if (!msgRecvList.empty())
{
return true;
}
return false;
});
commond = msgRecvList.front();
msgRecvList.pop_front();
myUniqueLock.unlock();
std::cout << "取出一个元素" << commond<<std::endl;
//std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
}
}
protected:
std::list<int> msgRecvList; //保存接收到的消息
std::mutex myMutex1;
std::condition_variable myCondition;
};
int main()
{
A obja;
std::thread myOutMsgObj(&A::outMsgRecvList, std::ref(obja));
std::thread myInMsgObj(&A::inMsgRecvList, std::ref(obja));
myOutMsgObj.join();
myInMsgObj.join();
return 0;
}
condition_variable条件变量是一个类
成员函数:
wait()第一个参数是unqinue_lock对象,第二个参数存在默认值
当wait()不存在第二个参数的时候,则会阻塞到本行并且对互斥量进行解锁,直到其他线程调用notify_one,当notify_one()被调用时,wait()函数尝试加锁,若加锁不成功,则会阻塞,加锁成功,就继续向下执行。
当wait()存在第二个参数的时候,如果第二个参数的值为false时,wait()函数会对互斥量进行解锁,并且阻塞到本行,直到其他线程调用notify_one(),如果第二个参数时true则wait()函数立即返回,程序继续向下执行
上述代码并非调用notify_one()函数的时候就立即进行操作,wait()函数需要先进行尝试加锁,如果加锁失败,并不会向下执行,直到加锁成功(需要于插入元素竞争互斥量),才会继续向下执行。(并非插入一个就取出一个)
std::thread myOutMsgObj1(&A::outMsgRecvList, std::ref(obja));
std::thread myOutMsgObj2(&A::outMsgRecvList, std::ref(obja));
std::thread myInMsgObj(&A::inMsgRecvList, std::ref(obja));
当一个输入对应两个输入的时候,notify_one()只会唤醒一个wait()函数,具体唤哪一个函数不确定。
1.2、notify_all()
作用:可以同时唤醒多个线程wait()函数,notify_one()只能唤醒一个线程
#include <iostream>
#include <thread>
#include <list>
#include <mutex>
#include <chrono>
class A
{
public:
void inMsgRecvList()
{
for (int i = 0; i < 100000; ++i)
{
std::cout << "inMsgRecvList()执行插入一个元素" << i<<std::endl;
std::unique_lock<std::mutex> myUniqueLock(myMutex1);
msgRecvList.push_back(i);
myCondition.notify_all();
myUniqueLock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
return;
}
void test()
{
while (1)
{
std::unique_lock<std::mutex> myUniqueLock(myMutex1);
myCondition.wait(myUniqueLock);
std::cout << "其他的业务线程Id = " <<std::this_thread::get_id()<< std::endl;
myUniqueLock.unlock();
}
}
void outMsgRecvList()
{
int commond = 0;
while (true)
{
{
std::unique_lock<std::mutex> myUniqueLock(myMutex1);
//wait()等待一个条件
/*如果第二个参数(lambda表达式)的返回值是false,那么wait()将解锁互斥量,并阻塞到本行
阻塞到其他某个线程调用notify_one()成员函数为止
如果第二个参数的返回值是true,则wait立即返回
如果wait()没有第二个参数,则会阻塞到本行解锁互斥量,直到某个线程调用notify_one();
当其他线程调用notify_one,wait()被唤醒,wait()不断地尝试互斥锁,如果获取不到,就阻塞
如果wait()有第二个参数,就判断返回值,如果是false(),解锁互斥量。再次等待被唤醒
如果表达式为true这立即返回,程序继续执行
如果没有第二个参数,被唤醒就向下执行
*/
myCondition.wait(myUniqueLock , [this] {
if (!msgRecvList.empty())
{
return true;
}
return false;
});
commond = msgRecvList.front();
msgRecvList.pop_front();
std::cout << "取出一个元素" << commond <<"\t线程id =" << std::this_thread::get_id() << std::endl;
myUniqueLock.unlock();
}
}
}
protected:
std::list<int> msgRecvList; //保存接收到的消息
std::mutex myMutex1;
std::condition_variable myCondition;
};
int main()
{
A obja;
std::thread myOutMsgObj1(&A::outMsgRecvList, std::ref(obja));
std::thread myOutMsgObj2(&A::test, std::ref(obja));
std::thread myInMsgObj(&A::inMsgRecvList, std::ref(obja));
myOutMsgObj1.join();
myOutMsgObj2.join();
myInMsgObj.join();
return 0;
}
通过结果可以发现同时唤醒了两个线程进行操作。