- condition_variable、wait、notify_one、notify_all
- 条件变量std::condition_variable、wait、notify_one
- 上述代码深入思考
- notify_all()
- async、future、packaged_task、promise
- std::async、std::future创建后台任务并返回值
- std::packaged_task
- std::promise
- 小结
条件变量std::condition_variable、wait()、notify_one():只能通知一个线程
线程A:等待一个条件满足
线程B:专门往消息队列中扔消息
std::condition_variable实际上是一个类,是一个和条件相关的一个类,说白了就是等待一个条件达成
这个类需要和互斥量来配合工作,用的时候我们要生成这个类的对象
class A
{
public:
//把收到消息(玩家命令)入到一个队列的线程
void inMsgRecvQueue() {
for (int i = 0; i < 10000; i++) {
cout << "inMsgRecvQueue()执行,插入一个元素!" << endl;
my_mutex.lock();
std::unique_lock<std::mutex> guard(my_mutex, std::adopt_lock);
msgRecvQueue.push_back(i);
//假如outMsgRecvQueue()正在处理一个事务,需要一段时间,而不是正卡在wait()那里等待你唤醒,那么此时这个notify_one没有效果
//my_cond.notify_one();//我们尝试把wait()的线程唤醒,执行完这行,那么outMsgRecvQueue()里面的wait就会被唤醒
my_cond.notify_all();
}
}
void outMsgRecvQueue() {
int command = 0;
while (true) {
std::unique_lock<std::mutex> guard(my_mutex);
my_cond.wait(guard, [this] {//wait()用来等一个东西,如果第二个参数返回值为true,那么wait()直接返回,执行结束。
//如果第二个参数lambda表达式返回值是false,那么wait()将解锁互斥量,并堵塞到本行
//那堵塞到什么时候为止呢?堵塞到其它线程调用notify_one()成员函数为止;
//如果wait()没有第二个参数:my_cond.wait(guard),就跟第二个参数lambda表达式返回false效果一样
//wait()将解锁互斥量,并堵塞到本行,堵塞到其它某个线程调用notify_one()成员函数为止;
if (!msgRecvQueue.empty()) {
return true;
}
return false;
});
//当其它线程用notify_one将本wait(原本是堵塞状态)唤醒后,wait就开始干活了,恢复后干什么活?
//a)wait()不断的尝试重新获取互斥量所,如果获取不到,那么流程就卡在wait这里等着获取,如果获取到了,那么wait()就继续执行b;
//b)
//b.1)如果wait有第二个参数(lambda),就判断这个lambda表达式,如果lambda表达式为false,那么wait()又将解锁互斥量,并堵塞到本行
//b.2)如果lambda表达式为true,则wait()返回,流程走下来(此时互斥锁被锁下来),
//b.3)如果wait()没有第二个参数,则wait返回,流程走下来。
//流程只要能走到这里来,这个互斥锁一定是锁着的,同时msgRecvQueue至少是有一条数据的。
command = msgRecvQueue.front();
msgRecvQueue.pop_front();
cout << "outMsgRecvQueue()执行,取出一个元素" << command<<"thread_id:"<<std::this_thread::get_id() << endl;
guard.unlock();//因为unique_lock的灵活性,所以我们可以随时unlock解锁,以免锁住太长时间
}
}
private:
std::list<int> msgRecvQueue;//容器,专门用于代表玩家发过来的命令。
std::mutex my_mutex;//创建了一个互斥量(一个互斥量就是一把锁)
std::condition_variable my_cond;//生成一个条件变量对象
};
//main
A a;
thread outThread1(&A::outMsgRecvQueue, &a);
thread outThread2(&A::outMsgRecvQueue, &a);
thread inThread(&A::inMsgRecvQueue, &a);
outThread1.join();
outThread2.join();
inThread.join();
cout << "I love China!" << endl;
上述代码深入思考
notify_all()
async、future、packaged_task、promise
std::async、std::future创建后台任务并返回值
希望线程返回一个结果
std::async是个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,返回一个std::future对象,std::future对象是个模板类。
这个std::future对象里面就含有线程入口函数所返回的结果(线程返回的结果),我们可以通过调用future对象的成员函数get()来获取结果。
future:将来的意思,有人也称呼std::future提供了一种访问异步 *** 作结果的机制,就是说这个结果你可能没有办法马上拿到。
在线程执行完毕时,你就能够拿到结果了,所以大家就这么理解,这个future对象会保存一个值,在将来的某个时刻能够拿到
下面的程序通过std::future对象的get()成员函数等待线程的执行结束并返回结果;get()不拿到将来的返回值,我就卡在这里等待拿值。
我们通过额外向std::async()传递一个参数,该参数的类型是std::launch类型(枚举类型),来达到一些特殊的目的
a)std::launch::deferred:表示线程入口函数调用被延迟到std::future()的wait()或者get()函数调用的时候才执行。
那如果wait(),或者get()没有被调用,那么线程会被执行吗?没执行,实际上,根本没创建
std::launch::defered:延迟调用,并且没有创建新线程,是在主线程中调用的线程入口函数。
b)std::launch::async,在调用async函数的时候就开始创建新线程。
std::async()函数,默认就是std::launch::async标记
class B{
public:
int mythread(int temp) {
cout << temp << endl;
cout << "mythread() start," << "threadid=" << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end," << "threadid=" << std::this_thread::get_id() << endl;
return 5;
}
};
//int mythread(int temp) {
// cout << temp << endl;
// cout << "mythread() start," << "threadid=" << std::this_thread::get_id() << endl;
// std::chrono::milliseconds dura(5000);
// std::this_thread::sleep_for(dura);
// cout << "mythread() end," << "threadid=" << std::this_thread::get_id() << endl;
// return 5;
//}
//main
B b;
int temp = 12;
cout << "main," << "threadid=" << std::this_thread::get_id() << endl;
//std::future result = std::async(mythread);
std::future<int> result = std::async(&B::mythread,&b,temp);
//result.wait()//卡在这里等待mythread执行完毕,本身不返回结果
//std::future result = std::async(std::launch::deferred,&B::mythread, &b, temp);
//实际上线程入口函数std::async()函数的第一个参数为std::launch::deferred时,在主线程中std::future::get()被调用时执行
//std::future result = std::async(std::launch::async, &B::mythread, &b, temp);
cout << result.get()<<endl;//卡在这里等待mythread()执行完毕,拿到结果。
//get只能调用一次,不能调用多次。
cout << "I love china!" << endl;
std::packaged_task
std::packaged_task:打包任务,把任务包装起来。
是个类模板,它的模板参数是各种可以调用的对象;通过std::packaged_task来把各种可调用对象包装起来,方便将来作为线程入口函数。
packaged_task包装起来的可调用对象,也是一个可调用对象。
1、mythread函数作为packaged_task的参数
int mythread(int temp) {
cout << temp << endl;
cout << "mythread() start," << "threadid=" << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end," << "threadid=" << std::this_thread::get_id() << endl;
return 5;
}
//main
cout << "main," << "threadid=" << std::this_thread::get_id() << endl;
std::packaged_task<int(int)> mypt(mythread);//我们把函数mythread通过packaged_task包装起来
std::thread mythread(std::ref(mypt), 1);
mythread.join();
std::future<int > result = mypt.get_future();//std::future对象里面包含有线程入口函数的返回结果,这里result保存mythread的返回值
cout << result.get() << endl;
2、lambda作为packageed_list的参数
std::packaged_task<int(int)> mypt([](int mypar) {
cout << mypar << endl;
cout << "mythread() start," << "threadid=" << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end," << "threadid=" << std::this_thread::get_id() << endl;
return 5;
});
std::thread mythread(std::ref(mypt), 1);
mythread.join();
std::future<int > result = mypt.get_future();
cout << result.get() << endl;
3、packageed_list对象放在容器中
packaged_task包装起来可调用对象后创建的对象,也是一个可调用对象
vector<std::packaged_task<int(int)>> v;
int main() {
std::packaged_task<int(int)> mypt([](int mypar) {
cout << mypar << endl;
cout << "mythread() start," << "threadid=" << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
cout << "mythread() end," << "threadid=" << std::this_thread::get_id() << endl;
return 5;
});
v.push_back(std::move(mypt));//入容器,这里用了移动语义,入进去之后mypt就为空
std::packaged_task<int(int)> mypt1 =std::move(*v.begin());//移动语义
v.erase(v.begin());
mypt1(123);
std::promise
std::promise,类模板
我们能够在某个线程中给它赋值,然后我们可以在其他线程中,把这个值取出来用
总结:通过promise保存一个值,在将来某个时刻我们通过把一个future绑定到这个promise上来得到这个绑定的值;
void mythread(std::promise<int> &mypromise, int temp) {//大家注意第一个参数
temp++;
temp *= 10;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
int result = temp;
mypromise.set_value(result);//结果我保存到了mypromise对象里面
}
void mythread2(std::future<int>& tempfu) {//大家注意第一个参数
int result=tempfu.get();
cout << "mythread2 result:" << result << endl;
return;
}
//main
std::promise<int> mypromise;
std::thread t1(mythread, std::ref(mypromise), 180);
t1.join();
//获取结果值
std::future<int> fu1 = mypromise.get_future();
std::thread t2(mythread2, std::ref(fu1));
t2.join();
int result = fu1.get();
cout << result << endl;
小结
到底怎么用?什么时候用?
我们学习这些东西的目的,并不是要把他们都用在我们的实际开发中。
相反,如果我们能够用最少的东西能够写出一个稳定的、高效的多线程程序,更值得赞赏;
我们为了成长,必须要阅读一些高手的代码,从而快速实现自己的代码积累,我们的技术就会有一个大幅度的提升。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)