今天小编给大家分享一下C++11 condition_variable条件变量怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
1 什么是条件变量
condition_variable是一个类,常和mutex搭配使用。
condition_variable类是一个同步原语,可用于阻塞一个线程或同时阻止多个线程,直到另一个线程修改共享变量并通知condition_variable。
防止多线程场景下,共享变量混乱。
理解条件变量要先理解三个概念:
锁 (锁住共享变量,线程独占)
wait
等待 (等待通知条件变量,变化的共享变量是否满足条件)notify
通知 (通知等待的条件变量,共享变量发送变化)
2 condition_variable类定义
2.1 wait函数
void wait( std::unique_lockstd::mutex& lock );//Predicate是lambda表达式。template< class Predicate >void wait( std::unique_lockstd::mutex& lock, Predicate pred );//以上二者都被notify_one())或notify_broadcast()唤醒,但是//第二种方式是唤醒后也要满足Predicate的条件。//如果不满足条件,继续解锁互斥量,然后让线程处于阻塞或等待状态。//第二种等价于while (!pred()){wait(lock);}
3 condition_variable用法
condition_variable必定至少有两方,一方是资源修改线程,一方是资源等待线程。就跟打篮球一样,同时篮球只会在一个人手中,投篮后就释放了篮球所有权,其他方就会抢夺篮球所有权。
3.1 资源修改线程步骤
获取一个mutex使用 std::unique_lock< std::mutex >
保持锁定状态,修改共享变量
condition_variable对象执行notify_one或者notify_all(notify_one/notify_all执行前可以释放锁)
3.2 资源等待线程步骤
获取一个mutex使用 std::unique_lock< std::mutex > unlock用于保护要修改的共享变量
检查条件变量,
(1)条件变量满足,线程继续执行
(2)条件变量不满足,wait会释放unlock锁,并挂起线程。
当notify通知条件变量、超时过期或发生虚假唤醒时,线程被唤醒,互斥锁unlock被原子地重新获取。然后,线程应该检查条件,如果唤醒是假的,则继续等待
4 代码示例
4.1 无需notify场景
当wait第一次执行是,条件已经满足,则程序不会阻塞(即无需notify),会直接向下执行。(仅为说明3.2 中第2点(1)的情况)
#include <iostream>#include <string>#include <thread>#include <mutex>#include <condition_variable>using namespace std;std::mutex m;std::condition_variable cv;std::string data;bool ready = false;bool processed = false; void worker_thread(){ std::cout << "3、worker_thread子线程开始执行" << endl; // Wait until main() sends data std::unique_lock<std::mutex> lk(m); std::cout << "4、worker_thread子线程获取到锁,条件满足无需notify,不阻塞向下执行" << endl; cv.wait(lk, []{return ready;}); // after the wait, we own the lock. data += " after processing"; // Send data back to main() processed = true; std::cout << "5、Worker thread signals data processing completed\n"; // Manual unlocking is done before notifying, to avoid waking up // the waiting thread only to block again (see notify_one for details) lk.unlock(); std::cout << "6、worker_thread子线程交出执行权限,主线程执行" << endl; std::this_thread::sleep_for(std::chrono::milliseconds(2000)); cv.notify_one(); std::cout << "9、worker_thread调用 notify_one" << endl;}int main(){ std::thread worker(worker_thread); std::cout << "1、主线程开始执行" << std::endl; data = "Example data"; // send data to the worker thread { //std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::lock_guard<std::mutex> lk(m); ready = true; } std::cout << "2、锁已经释放了,主线程休眠,子线程执行" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); //cv.notify_one(); { std::cout << "7、主线程data:" << data << endl; std::unique_lock<std::mutex> lk(m); std::cout << "8、主线程条件满足无需notify" << endl; cv.wait(lk, []{return processed;}); } worker.join(); std::cout << "10、主线程结束" << endl;}
执行结果:
4.2 正常应用场景1
#include <iostream>#include <string>#include <thread>#include <mutex>#include <condition_variable>using namespace std;std::mutex m;std::condition_variable cv;std::string data;bool ready = false;bool processed = false; void worker_thread(){ std::cout << "3、worker_thread子线程开始执行" << endl; // Wait until main() sends data std::unique_lock<std::mutex> lk(m); std::cout << "4、worker_thread子线程获取到锁,条件不满足,释放lk锁,子线程阻塞" << endl; cv.wait(lk, []{return ready;}); std::cout << "8、worker_thread子线程获取到锁,子线程继续执行" << endl; // after the wait, we own the lock. data += " after processing"; // Send data back to main() processed = true; std::cout << "9、Worker thread signals data processing completed\n"; // Manual unlocking is done before notifying, to avoid waking up // the waiting thread only to block again (see notify_one for details) lk.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(5000)); std::cout << "10、worker_thread调用 notify_one通知主线程执行" << endl; cv.notify_one();}int main(){ std::thread worker(worker_thread); std::cout << "1、主线程开始执行" << std::endl; data = "Example data"; // send data to the worker thread { std::cout << "2、主线程休眠,子线程进入执行" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::cout << "5、主线程结束休眠,主线程获取lk锁,进入执行" << std::endl; std::lock_guard<std::mutex> lk(m); ready = true; } std::cout << "6、主线程释放lk,调用notify通知子线程" << std::endl; cv.notify_one(); { std::cout << "7、由于主线程的执行时钟周期未结束,继续执行主线程获取lk, wait检查条件不满足,释放锁" << endl; std::unique_lock<std::mutex> lk(m); cv.wait(lk, []{return processed;}); } worker.join(); std::cout << "11、主线程结束" << endl;}
执行结果:
这里notify执行后不一定立即执行子线程,如果cpu执行时钟周期未结束,则主线程会继续执行. 所以7,8,9,10顺序可能变化参见4.3
同时4.1也会因为cpu时钟周期,执行顺序有所变动。
4.3 正常应用场景2
#include <iostream>#include <string>#include <thread>#include <mutex>#include <condition_variable>using namespace std;std::mutex m;std::condition_variable cv;std::string data;bool ready = false;bool processed = false; void worker_thread(){ std::cout << "3、worker_thread子线程开始执行" << endl; // Wait until main() sends data std::unique_lock<std::mutex> lk(m); std::cout << "4、worker_thread子线程获取到锁,条件不满足,释放lk锁,子线程阻塞" << endl; cv.wait(lk, []{return ready;}); std::cout << "8、worker_thread子线程获取到锁,子线程继续执行" << endl; // after the wait, we own the lock. data += " after processing"; // Send data back to main() processed = true; std::cout << "9、Worker thread signals data processing completed\n"; // Manual unlocking is done before notifying, to avoid waking up // the waiting thread only to block again (see notify_one for details) lk.unlock(); std::cout << "10、worker_thread调用 notify_one通知主线程执行" << endl; cv.notify_one();}int main(){ std::thread worker(worker_thread); std::cout << "1、主线程开始执行" << std::endl; data = "Example data"; // send data to the worker thread { std::cout << "2、主线程休眠,子线程进入执行" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::cout << "5、主线程结束休眠,主线程获取lk锁,进入执行" << std::endl; std::lock_guard<std::mutex> lk(m); ready = true; } std::cout << "6、主线程释放lk,调用notify通知子线程" << std::endl; cv.notify_one(); { for(int i = 0; i< 10000000; i++) { int j = i; } std::cout << "7、由于主线程的执行时钟周期未结束,继续执行主线程获取lk, wait检查条件不满足,释放锁" << endl; std::unique_lock<std::mutex> lk(m); cv.wait(lk, []{return processed;}); } worker.join(); std::cout << "11、主线程结束" << endl;}
执行结果:
以上就是“C++11 condition_variable条件变量怎么使用”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。