C++11 condition_variable条件变量的用法说明
作者:kingforyang 发布时间:2021-12-06 23:39:56
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;
}
执行结果:
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
来源:https://blog.csdn.net/kingforyang/article/details/121665393


猜你喜欢
- 开发Android应用中,随着功能越来越多,启动速度越来越慢。有没有办法让自己应用启动速度快一点呢?方法是人想出来的。先说说我的实现方法:1
- 实践过程效果代码public partial class Form1 : Form{ public Form1()
- 本文实例实现了C#一键换IP、重置DNS、网关及掩码的功能,具体实现的功能为在程序界面窗口中设置ip地址和子网掩码,设置网关地址,设置DNS
- 传统“长轮询”实现Web端即时通讯的问题WebSocket出现之前,Web端为了实现即时通讯,所用的技术都是Ajax轮询(polling)。
- 一、@RequestMapping注解的功能从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方
- 本文实例讲述了C#多线程处理多个队列数据的方法。分享给大家供大家参考。具体实现方法如下:using System;using System.
- Java * 分析及理解代理设计模式定义:为其他对象提供一种代理以控制对这个对象的访问。 * 使用java * 机制以巧妙的方式实现了
- JNI,Java Native Interface,是 native code 的编程接口。JNI 使 Java 代码程序可以与 nativ
- C#中常涉及到对用户密码的加密于解密的算法,其中使用MD5加密是最常见的的实现方式。本文总结了通用的算法并结合了自己的一点小经验,分享给大家
- 一.代码实现1. “Activity_11\src\yan\activity_11\MainActivity.java”pack
- 本文设计一个简单的班级管理系统,满足如下要求:1、设计学生类Student,包含学号(String型)、姓名(String型)、
- 安卓原生的按钮是多么丑,效果是多么单调,大家也是有目共睹的。 要做一个APP少不了使用按钮,一个好看的按钮少不了好看的效果和外表,这次主要跟
- 本文是针对AndBase框架学习整理的第二篇笔记,想要了解AndBase框架的朋友可以阅读本文,大家共同学习。使用AbActivity内部封
- 前言相信做地图社交类APP开发的大家都知道,一般情况下,为了整体的美观和用户体验度,我们需要定制化Marker的样式。本文中实现的方式都是基
- 项目需要去调用.NET的WebSrevice,本身是Java,研究了半天,终于有些头绪,记下来。1,新建.NET WebService。只在
- 前言在单机应用时代,我们对一个共享的对象进行多线程访问的时候,使用java的synchronized关键字或者ReentrantLock类对
- 今天跟大家分享一个实现PDF另存为的效果,是调用Acrobat SDK的JavaScript实现的。/// <summary>&
- 本文实例为大家分享了Android实现双曲线折线图的具体代码,供大家参考,具体内容如下先看一下效果图1.先下载jar包 mpandroidc
- iText下载页面: http://sourceforge.net/projects/itext/files/1.创建简单的PDF文件pac
- 前段时间分享了《阅读跟踪 Java 源码的几个小技巧》是基于 Eclipse 版本的,看大家的留言都是想要 IDEA 版本的源码阅读技巧。所