我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++11 condition_variable条件变量的用法说明

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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;
}

执行结果:

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。 

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++11 condition_variable条件变量的用法说明

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++11 condition_variable条件变量怎么使用

今天小编给大家分享一下C++11 condition_variable条件变量怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解
2023-07-02

C++11中final说明符的用法

本篇内容主要讲解“C++11中final说明符的用法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++11中final说明符的用法”吧!当我们声明/定义了一个虚函数时候,派生类可以选择覆盖这个
2023-06-19

C++11中线程、锁和条件变量的介绍

这篇文章主要介绍“C++11中线程、锁和条件变量的介绍”,在日常操作中,相信很多人在C++11中线程、锁和条件变量的介绍问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++11中线程、锁和条件变量的介绍”的疑
2023-06-17

C++11中线程锁和条件变量的示例分析

这篇文章主要介绍了C++11中线程锁和条件变量的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。线程std::thread类, 位于头文件,实现了线程操
2023-06-15

怎么理解C++11 中的线程及锁和条件变量

今天就跟大家聊聊有关怎么理解C++11 中的线程及锁和条件变量,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。线程类std::thread代表一个可执行线程,使用时必须包含头文件
2023-06-17

c++多线程要使用条件变量的原因

小编给大家分享一下c++多线程要使用条件变量的原因,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!先看示例1:#include #include
2023-06-15

[教程]JScript中的变量引用规则(补充,函数用法说明) (转)

[教程]JScript中的变量引用规则(补充,函数用法说明) (转)[@more@][教程]jscript中的变量引用规则(补充,函数用法说明)by .NET.com/,http://lostinet.d2g.com">http://www
2023-06-03

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录