C++线程同步(下)

多线程同步

  • 概述
  • 信号量
    • 示例一
      • 代码实现
      • 运行结果
      • 分析
    • 示例二
      • 开发环境
      • 代码实现
      • 运行结果
      • 分析
  • future和promise
    • 示例一
      • 实现代码
      • 运行结果
      • 分析
    • 示例二
      • 实现代码
      • 运行结果
    • 示例三
      • 实现代码
      • 运行结果
    • 示例四
      • 示例代码
      • 运行结果
      • 注意
      • 附加
      • 示例扩展
        • 实现代码
        • 运行结果
        • 注意
        • 扩展
  • 原子
    • 应用场景
    • 头文件
    • 示例
      • 实现代码
      • 运行结果
      • 分析

概述

本位承接上一篇多线程同步(上),接着讲解剩下的C++多线程同步的三种方式:信号量、future和promise,原子操作。

信号量

在C++中,信号量(semaphore)是一种同步机制,用于控制多个线程对共享资源的访问。信号量通常用于限制对共享资源的并发访问数量,或者用于实现线程之间的同步。

信号量的工作原理类似于一个计数器,它维护着一个整数值。当一个线程需要访问共享资源时,它会尝试获取信号量。如果信号量的值大于零,那么线程将信号量的值减一,并继续执行。如果信号量的值为零,那么线程将被阻塞,直到信号量的值大于零为止。

当一个线程完成对共享资源的访问后,它会释放信号量,即将信号量的值加一。这允许其他等待的线程获取信号量并访问共享资源。

示例一

因为C++11及其标准库中并没有提供原生的信号量类。但C++20特性中提供了信号量类。
在C++20之前,C++标准库没有提供原生的信号量类。相反,开发者通常使用std::condition_variable和std::mutex来模拟信号量的行为,或者依赖第三方库或平台特定的实现来获取信号量的功能。

代码实现

信号量实际上应用计数的原理,用于控制对共享资源的并发访问数量。它允许一定数量的线程同时访问资源,当达到这个数量时,其他尝试获取资源的线程将会被阻塞,直到有线程释放了资源。

需设置可供访问共享资源的最大线程数量,每有一个线程访问,信号量就减一,访问结束后加一。在这个过程中若是信号量的个数为0,则要访问的线程会被阻塞,等待其它正在访问的线程结束后,信号量加一,信号量不为0时才可访问。

下面是使用条件变量和互斥锁实现的信号量。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>using namespace std;const int g_n = 3;
mutex g_mutex;
condition_variable g_cond;
int g_count = 0;void increase() {for (int i = 0; i < 5; ++i) {unique_lock<mutex> lock(g_mutex);g_cond.wait(lock, [] {return g_count < g_n; });++g_count;cout << "increase" << g_count << endl;g_cond.notify_all();}
}void decrease() {for (int i = 0; i < 5; ++i) {unique_lock<mutex> lock(g_mutex);g_cond.wait(lock, [] {return g_count > 0; });cout << "decrease:" << g_count << endl;--g_count;g_cond.notify_all();}
}int main()  
{thread t1(decrease);thread t2(increase);t1.join();t2.join();cout << "主线程的id:" << this_thread::get_id() << endl;return 0;
}

运行结果

在这里插入图片描述

分析

这里使用互斥锁和条件变量来模拟信号量。加计数中当g_count > g_n,即g_count 的数值大于3的时候阻塞等待,释放锁。加计数线程处理函数释放锁之后,减计数线程处理函数开始获取锁,当g_count > 0,即g_count的值大于0,继续向下执行。相反g_count 的值小于等于0则减计数线程处理函数阻塞等待,释放锁。

其中有一个环节就是当其中的一个线程处理函数被阻塞等待,另一个线程处理函数获得锁之后执行到最后会调用notify_all()来唤醒阻塞等待的线程,但这时候被唤醒的线程能不能得到锁就取决与线程,操作系统的调度策略

下面详细分析下运行结果下的程序运行过程。

  1. t1线程启动减计数线程处理函数,获取锁,执行到第二行wait时,由于不满足g_count 大于0的条件(g_count 此时为0),阻塞等待,并释放锁。
  2. t2线程的加计数线程处理函数获取锁,满足wait条件:g_count 小于3。不用等待继续向下执行,增加g_count 的值并输出。最后唤醒被阻塞的减计数wait,并在i=0这个循环结束为开其i=1的短暂瞬间释放锁。
  3. 减计数wait被唤醒,但是没有抢到锁。只能继续等待。
  4. 加计数函数继续i=1的循环,创建unique_lock获取锁,条件满足,继续向下执行。结束i=2的循环前唤醒减计数的wait,当结束本次循环,未在下次循环创建unique_lock获取锁之前,减计数函数都可以去抢锁。但实际上没有抢到锁。只能继续等待。
  5. 当i =3时,加计数的wait中条件不满足(false),阻塞等待,释放锁。
  6. 减计数的wait获得锁(因为之前已经被唤醒,所以减计数线程处理函数处于就绪状态,一旦有锁被释放,就会尝试获取锁),满足g_count 大于0,向下继续执行,每个循环中g_count 每次减1,当条件不再满足,就再次释放锁,阻塞等待。
  7. 此时加计数因为之前已被减计数唤醒,处于就绪状态,减计数一旦释放锁,加计数就会获得锁,满足条件,向下执行。唤醒减计数,但减计数没有获得锁。直到加计数的循环次数达到5次,结束循环,释放锁。
  8. 减计数获得锁,满足条件,向下执行。直到for循环结束。

示例二

这个示例需要支持c++20,使用semaphore类。

开发环境

vs2019,且将项目属性设置为支持c++20。
在这里插入图片描述

代码实现

本例比价简单,主要是理解其如何使用,及其思想。
下面为代码:

#include <iostream>
#include <thread>
#include <semaphore>//C++20
#include <mutex>using namespace std;counting_semaphore g_sem(3);
mutex g_mutex;
#define NUM 5void fun(int i) {g_sem.acquire();g_mutex.lock();//为了输出的线程不被其它访问的线程中断cout << "子线程" <<this_thread::get_id()<<"编号"<<i<< endl;g_mutex.unlock();this_thread::sleep_for(chrono::milliseconds(10));//为了模拟操作耗时g_sem.release();
}
int main()  
{thread threads[NUM];for 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/703122.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【蓝桥杯】拓扑排序

一.拓扑排序 1.定义&#xff1a; 设G&#xff08;V&#xff0c;E&#xff09;是一个具有n个顶点的有向图&#xff0c;V中的顶点序列称为一个拓扑序列&#xff0c;当且仅当满足下列条件&#xff1a;若从顶点到有一条路径&#xff0c;则在顶点序列中顶点必在之前。 2.基本思想…

prime_series_level-1靶场详解

环境搭建 官网https://www.vulnhub.com/entry/prime-1,358/ 直接导入靶机 解题思路 arp-scan -l 确认靶机ip为192.168.236.136 也可以使用nmap扫网段 nmap -sn 192.168.236.0/24 使用nmap扫描靶机开放的端口 nmap -sS -T5 --min-rate 10000 192.168.236.136 -sC -p- &#xf…

【项目部署上线】宝塔部署前端Docker部署后端

【项目部署上线】宝塔部署前端&Docker部署后端 文章目录 【项目部署上线】宝塔部署前端&Docker部署后端1.安装依赖1.1 安装mysql1.2 安装Canal1.3 安装redis1.4 安装rabbitmq1.5 安装nacos 2. 部署前端3. 部署后端 1.安装依赖 1.1 安装mysql docker run -d -p 3306:3…

Redis主从、哨兵、Redis Cluster集群架构

Redis主从、哨兵、Redis Cluster集群架构 Redis主从架构 Redis主从架构搭建 主从搭建的问题 如果同步数据失败&#xff0c;查看log日志报错无法连接&#xff0c;检查是否端口未开放出现”Error reply to PING from master:...“日志&#xff0c;修改参数protected-mode no …

S32 Design Studio PE工具配置TMR

配置步骤 配置内容 生成的配置结构体如下&#xff0c;在Generated_Code路径下的lpTmr.c文件和lpTmr.h文件。 /*! lpTmr1 configuration structure */ const lptmr_config_t lpTmr1_config0 {.workMode LPTMR_WORKMODE_PULSECOUNTER,.dmaRequest false,.interruptEnable tr…

C++力扣题目 1143--最长公共子序列 1035--不相交的线 53--最大子数组和

1143.最长公共子序列 力扣题目链接(opens new window) 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长公共子序列的长度。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符&#xff0…

力扣用例题:2的幂

此题的解题方法在于根据用例调整代码 bool isPowerOfTwo(int n) {if(n1){return true;}if(n<0){return false;}while(n>2){if(n%21){return false;}nn/2; }if(n1){return false;}return true;}

Spring的另一大的特征:AOP

目录 AOP &#xff08;Aspect Oriented Programming&#xff09;AOP 入门案例&#xff08;注解版&#xff09;AOP 工作流程——代理AOP切入点表达式AOP 通知类型AOP通知获取数据获取切入点方法的参数获取切入点方法返回值获取切入点方法运行异常信息 百度网盘分享链接输入密码数…

FPGA 与 数字电路的关系 - 这篇文章 将 持续 更新 :)

先说几个逻辑&#xff1a;&#xff08;强调一下在这篇文章 输入路数 只有 1个或2个&#xff0c;输出只有1个&#xff0c;N个输入M个输出以后再说&#xff09; 看下面的几个图&#xff1a; 图一&#xff08; 忘了 这是 啥门&#xff0c;不是门吧 &#xff1a;&#xff09;也就…

2024-02-25 Unity 编辑器开发之编辑器拓展7 —— Inspector 窗口拓展

文章目录 1 SerializedObject 和 SerializedProperty2 自定义显示步骤3 数组、List 自定义显示3.1 基础方式3.2 自定义方式 4 自定义属性自定义显示4.1 基础方式4.2 自定义方式 5 字典自定义显示5.1 SerizlizeField5.2 ISerializationCallbackReceiver5.3 代码示例 1 Serialize…

【新三板年报文本分析】第一辑:python+selium模拟浏览器,批量实现上市公司年报链接

目录 序言函数模块介绍创建模拟浏览器对象只需要执行一次的部分需要批量执行的重复操作部分&#xff08;信息录入excel&#xff09;换页操作主函数 本地文件结构全部代码结果预览 如果直接需要结果的&#xff0c;可以直接见文末&#xff0c;获取资源。 序言 新三板年报链接&am…

emoji选择器

emoji表情 功能描述 这款聊天对话时选择表情的UI插件&#xff0c;是为了提升用户在聊天过程中的互动体验而设计的。它提供了一个直观且易于操作的界面&#xff0c;使用户能够快速地选择并插入各种表情符号&#xff0c;从而丰富他们的聊天内容&#xff0c;增加情感表达的多样性…

Unity3D 使用 Proto

一. 下载与安装 这里下载Google Protobuff下载 1. 源码用来编译CSharp 相关配置 2. win64 用于编译 proto 文件 二. 编译 1. 使用VS 打开 2. 点击最上面菜单栏 工具>NuGet 包管理器>管理解决方案的NuGet 管理包 版本一定要选择咱们一开始下载的对应版本否则不兼容&am…

C语言第三十一弹---自定义类型:结构体(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 目录 1、结构体内存对齐 1.1、为什么存在内存对齐? 1.2、修改默认对齐数 2、结构体传参 3、结构体实现位段 3.1、什么是位段 3.2、位段的内存分配 3.3、…

幻兽帕鲁服务器多少钱?有买过的吗?

幻兽帕鲁服务器多少钱&#xff1f;太卷了&#xff0c;降价到24元1个月&#xff0c;阿里云4核16G10M游戏服务器26元1个月、149元半年&#xff0c;腾讯云4核16G游戏服务器32元、312元一年&#xff0c;华为云26元&#xff0c;京东云主机也是26元起。云服务器吧yunfuwuqiba.com给大…

VUE3环境搭建开发准备

VUE3 Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&#xff0c;Vu…

【Prometheus】概念和工作原理介绍

目录 一、概述 1.1 prometheus简介 1.2 prometheus特点 1.3 prometheus架构图 1.4 prometheus组件介绍 1、Prometheus Server 2、Client Library 3、pushgateway 4、Exporters 5、Service Discovery 6、Alertmanager 7、grafana 1.5 Prometheus 数据流向 1.6 Pro…

100天精通Python(实用脚本篇)——第117天:基于selenium实现反反爬策略之代码输入账号信息登录网站

文章目录 专栏导读1. 前言2. 实现步骤3. 基础补充4. 代码实战4.1 创建连接4.2 添加请求头伪装浏览器4.3 隐藏浏览器指纹4.4 最大化窗口4.5 启动网页4.6 点击密码登录4.7 输入账号密码4.8 点击登录按钮4.9 完整代码4.10 GIF动图展示 五、总结 专栏导读 &#x1f525;&#x1f5…

IDEA安装配置以及安装配置Maven

IEDA官方下载地址&#xff0c;有专业版&#xff08;收费&#xff0c;破解&#xff09;&#xff0c;社区版&#xff08;免费&#xff09; 下载 IntelliJ IDEA – 领先的 Java 和 Kotlin IDE 安装配置Maven 1.解压apache-maven-3.6.3-bin.zip&#xff0c;安装maven到D盘softwar…

台湾旺泓-WH4530A三合一光距感 接近传感芯片

WH4530A是一款集成了环境光传感器&#xff08;PS&#xff09;接近传感器&#xff08;ALS&#xff09;和红外LED灯三合一为一体的光距感传感芯片&#xff0c;可以测距从0到100厘米以内范围&#xff1b;并采用I2C接口具有超高的灵敏度和精准的测距检测范围。 该WH4530A​​​​​…