返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++11 condition_variable条件变量的用法说明
  • 823
分享到

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

2024-04-02 19:04:59 823人浏览 泡泡鱼
摘要

目录1 什么是条件变量2 condition_variable类定义2.1 wait函数3 condition_variable用法3.1 资源修改线程步骤3.2 资源等待线程步骤4

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

执行结果:

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

--结束END--

本文标题: C++11 condition_variable条件变量的用法说明

本文链接: https://lsjlt.com/news/154121.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C++11 condition_variable条件变量的用法说明
    目录1 什么是条件变量2 condition_variable类定义2.1 wait函数3 condition_variable用法3.1 资源修改线程步骤3.2 资源等待线程步骤4...
    99+
    2024-04-02
  • C++11 condition_variable条件变量怎么使用
    今天小编给大家分享一下C++11 condition_variable条件变量怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们...
    99+
    2023-07-02
  • C++11中final说明符的用法
    本篇内容主要讲解“C++11中final说明符的用法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++11中final说明符的用法”吧!当我们声明/定义了一个虚函数时候,派生类可以选择覆盖这个...
    99+
    2023-06-19
  • 详解C++11中的线程锁和条件变量
    目录线程锁条件变量小结线程 std::thread类, 位于<thread>头文件,实现了线程操作。std::thread可以和普通函数和 lambda 表达式搭配使用。...
    99+
    2024-04-02
  • C++11中线程、锁和条件变量的介绍
    这篇文章主要介绍“C++11中线程、锁和条件变量的介绍”,在日常操作中,相信很多人在C++11中线程、锁和条件变量的介绍问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++11中线程、锁和条件变量的介绍”的疑...
    99+
    2023-06-17
  • C++11的override说明符怎么使用
    本篇内容主要讲解“C++11的override说明符怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++11的override说明符怎么使用”吧!多态和虚函数多态是C++的重要特征之一,...
    99+
    2023-06-19
  • C++11线程、互斥量及条件变量怎么创建
    这篇“C++11线程、互斥量及条件变量怎么创建”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C++11线程、互斥量及条件变量...
    99+
    2023-07-05
  • C++11中线程锁和条件变量的示例分析
    这篇文章主要介绍了C++11中线程锁和条件变量的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。线程std::thread类, 位于<thread>头文件,...
    99+
    2023-06-15
  • 怎么理解C++11 中的线程及锁和条件变量
    今天就跟大家聊聊有关怎么理解C++11 中的线程及锁和条件变量,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。线程类std::thread代表一个可执行线程,使用时必须包含头文件<...
    99+
    2023-06-17
  • C++11之std::future对象的使用以及说明
    目录std::future介绍细节说明wait系列操作get操作时序图std::future使用总结std::future介绍 在前面几篇文章中基本都用到thread对象,它是C++...
    99+
    2023-02-28
    C++11 std::future std::future对象 std::future对象使用
  • WordPress自带的条件标签使用说明
    WordPress自带的条件标签可以让你依据条件显示不同的内容,比如,你可以检查用户是在首页是否登陆 php if(语句) 用php的条件语句你可以判断一些事情的真假,如果是真,代码将被执行,否则什么也不发生.看下面的语...
    99+
    2022-06-12
    条件标签 标签说明
  • C++中forauto的用法及说明
    目录C++中for auto的用法C++ for(auto &a:b)、for(auto a:b)、for(const auto &a:b)总结C++中for aut...
    99+
    2023-02-25
    C++ for auto for auto的用法 for auto说明
  • C++中new的用法及说明
    目录C++ new的用法知识点小结new的三种使用方法概念示例C++ new的用法 知识点小结 new其实就是告诉计算机开辟一段新的空间,但是和一般的声明不同的是,new开辟的空间在...
    99+
    2022-11-13
    C++中new new的用法 C++ new用法
  • R语言 Factor类型的变量使用说明
    factor类型的创建 1. factor( ) > credit_rating <- c("BB", "AAA", "AA", "CCC", "AA", "AAA"...
    99+
    2024-04-02
  • vue-cli环境变量process.env的使用及说明
    目录vue-cli 环境变量 process.env使用vue-cli配置环境变量process.env.xxx总结vue-cli 环境变量 process.env使用 参考官网:&...
    99+
    2022-12-08
    vue-cli环境变量使用 process.env的使用 vue-cli环境变量
  • c++多线程要使用条件变量的原因
    小编给大家分享一下c++多线程要使用条件变量的原因,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!先看示例1:#include <iostream&...
    99+
    2023-06-15
  • springboot配置文件属性变量引用方式${}和@@用法及区别说明
    目录配置文件属性变量引用${}和@@用法${}常用于pom.xml@@方式常用于引用springboot非默认配置文件配置文件中的“@”问题springboo...
    99+
    2024-04-02
  • Shell常用的特殊位置参数变量说明
    $0    获取当前执行的shell脚本的文件名,如果执行脚本包含了路径,那么就包括脚本路径$n    获取当前执行的shell脚本的第n个参数值,n=1..9,当n为0时表示脚本...
    99+
    2023-06-06
  • c#中Invoke与BeginInvoke的用法及说明
    目录c# Invoke与BeginInvoke先讲下InvokeBeginInvoke总结c# Invoke与BeginInvoke 最近在学习线程时,发现当我创建的线程需要访问UI...
    99+
    2023-01-28
    c# Invoke c# BeginInvoke c# Invoke与BeginInvoke
  • [教程]JScript中的变量引用规则(补充,函数用法说明) (转)
    [教程]JScript中的变量引用规则(补充,函数用法说明) (转)[@more@][教程]jscript中的变量引用规则(补充,函数用法说明)by .NET.com/,http://lostinet.d2g.com">http://www...
    99+
    2023-06-03
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作