C++ 11新特性之并发
概述
随着计算机硬件的发展,多核处理器已经成为主流,对程序并发执行能力的需求日益增长。C++ 11标准引入了一套全面且强大的并发编程支持库,为开发者提供了一个安全、高效地利用多核CPU资源进行并行计算的新框架,极大地简化了多线程开发。
std::thread
在C++ 11中,std::thread是用于创建和管理线程的核心组件。使用线程的一些要点如下。
1、创建线程。
通过调用std::thread构造函数,传入要在线程中执行的函数(或可调用对象)以及任何必要的参数来创建线程。
2、线程函数。
可以是一个全局函数、类成员函数(此时需要传递指向该类实例的指针或引用),或者是一个满足Callable要求的类型。
3、线程执行。
一旦创建了std::thread对象,线程就会尝试启动执行,但具体何时开始执行由操作系统调度决定。
4、线程同步。
若多个线程共享数据,通常需要使用互斥锁(std::mutex)、条件变量(std::condition_variable)或其他同步机制来避免竞态条件和数据不一致问题。
5、线程生命周期管理。
join()方法会阻塞当前线程,直到被调用join()的线程完成其任务。detach()方法将线程从std::thread对象中分离,使其成为一个守护线程,当主线程退出而未调用join()时,这个分离的线程仍然可以继续运行。但是,如果分离的线程最后仍在运行且没有其他引用,则可能会导致资源泄漏。
std::thread的具体使用,可参考下面的示例代码。
#include <iostream>
#include <thread>
using namespace std;void ThreadFunc()
{cout << "Sub thread" << endl;
}int main() {thread myThread(ThreadFunc);cout << "Main thread init" << endl;myThread.join();cout << "Main thread exit" << endl;return 0;
}
std::mutex
std::mutex是用于实现线程间同步的基础工具,它确保同一时间内只有一个线程能够访问被保护的资源或执行一段代码。为了简化锁的管理并防止死锁,通常建议使用std::lock_guard或std::unique_lock这样的RAII(Resource Acquisition Is Initialization)机制来自动管理锁的生命周期。同时,在复杂的情况下,还可以结合条件变量等工具来实现更为灵活的线程同步逻辑。
在<mutex>头文件中定义了std::mutex类型,它代表一个可重入互斥量。当多个线程试图同时获取已锁定的互斥量时,除了已经持有该互斥量的线程外,其他线程会被阻塞直到互斥量被解锁。std::mutex的主要成员函数如下。
1、构造函数:默认构造一个未锁定的互斥量。
std::mutex mtx;
2、lock():将互斥量锁定,如果互斥量已经被另一个线程锁定,则调用此方法的线程将被阻塞,直至互斥量变为可用。
mtx.lock();
3、unlock():解锁互斥量,允许等待的线程(如果有)获得所有权并继续执行。
mtx.unlock();
4、try_lock():尝试锁定互斥量,但不会阻塞。如果成功获取锁则返回true,否则(即互斥量已被锁定)立即返回false。
if (mtx.try_lock())
{// 已经获取到锁
}
else
{// 未能获取到锁
}
5、RAII包装器类。为了确保即使在异常情况下也能正确释放互斥锁,C++ 11提供了几个基于RAII(Resource Acquisition Is Initialization)原则的包装器类,比如:std::lock_guard和std::unique_lock。
std::lock_guard:当lock_guard对象创建时自动锁定互斥量,并在其析构时自动解锁互斥量,从而避免忘记解锁导致的死锁问题。
std::lock_guard<std::mutex> lock(mtx);
// 这里是受保护的代码区域
// lock析构时,自动解锁
std::unique_lock:提供了比lock_guard更多的灵活性,比如:手动锁定、解锁以及尝试锁定等。
std::condition_variable
condition_variable是C++标准库中的一个同步原语,它是多线程编程中的一种关键工具,主要用于线程间的通信和同步。它与互斥量(mutex)配合使用,可以实现线程的等待和通知机制。具体来说,std::condition_variable类提供了以下的功能。
1、等待。
当某个条件不满足时,线程可以调用wait()函数释放互斥锁并进入等待状态,直到其他线程对同一个条件变量调用notify_one()或notify_all()函数唤醒它。
2、唤醒。
notify_one():唤醒一个正在等待此条件变量的线程(如果有多个线程在等待,则唤醒其中一个)。
notify_all():唤醒所有正在等待此条件变量的线程。
通常,condition_variable的典型使用场景包括:生产者/消费者模式、 barrier同步等,通过它可以有效地控制线程在满足特定条件时才继续执行,从而避免无效的循环检查或者竞争条件等问题。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;static mutex s_mutex;
static condition_variable s_condition;
static bool s_bReady = false;void PrintID(int nID)
{unique_lock<mutex> lock(s_mutex);while (!s_bReady){// 当ready为false时,线程会一直等待s_condition.wait(lock);}// 当其他线程修改ready为true,并调用cv.notify_all()后,这里会被唤醒cout << "Thread " << nID << " is running." << endl;
}void Process()
{unique_lock<mutex> lock(s_mutex);s_bReady = true;// 唤醒所有等待的线程s_condition.notify_all();
}int main()
{thread pThreads[10];for (int i = 0; i < 10; ++i){pThreads[i] = thread(PrintID, i + 1);}// 创建一个线程来修改ready并唤醒其他线程thread threadOther(ref(Process));for (auto& t : pThreads){t.join();}threadOther.join();return 0;
}
在上面的示例代码中,我们创建了10个线程,它们都在等待一个条件:s_bReady变为true。当Process线程将s_bReady设为true并调用s_condition.notify_all()后,所有等待的线程都会被唤醒,并打印自己的ID。
std::atomic
std::atomic是C++ 11引入的标准库中的一个模板类,它提供了一种能够在多线程环境中进行原子操作的类型安全方式。原子操作意味着:即使在没有互斥量或其他同步机制的情况下,该操作也能够从多个线程中以不可分割的方式执行,即不会出现半个操作的现象,确保了数据一致性。
使用std::atomic可以有效地处理简单的同步需求,比如:无锁计数器、标志位等,并且相比传统的互斥锁而言,其开销通常更小,性能更高。
#include <atomic>
#include <thread>
#include <cassert>
using namespace std;static atomic<int> s_nCounter(0);void Increment()
{++s_nCounter;
}int main()
{thread t1(Increment);thread t2(Increment);t1.join();t2.join();// 这个断言总是成立,因为s_nCounter的递增是原子的assert(s_nCounter == 2);return 0;
}
std::atomic支持多种类型的对象,包括但不限于:基本内置类型、指针以及用户自定义类型(如果满足特定条件)。它提供了load、store、exchange、compare_exchange_strong/weak等一系列原子操作方法,用于读写和更新其内部封装的数据成员。
std::future和std::async
std::future 和 std::async是C++ 11标准引入的异步编程工具,它们位于<future>头文件中,用于简化并发任务的管理和结果的获取。
std::future是一个模板类,它代表了一个可以在未来某个时间点获取的结果。当你启动一个异步计算时,该计算的结果可以通过std::future对象来访问。std::future 提供了以下功能。
获取结果:调用std::future::get()会阻塞当前线程,直到异步计算完成并返回结果。
检查是否已准备好:可以检查future是否已经包含有效结果或异常。
取消异步操作:虽然不能直接取消异步操作,但可以关联一个可取消的共享状态,然后取消那个状态。
获取异常:如果异步计算过程中抛出了异常,则可以在future上捕获到这个异常。
std::async是一个函数模板,它用来异步执行一个函数,并返回一个表示其结果的std::future对象。
#include <iostream>
#include <future>
#include <chrono>
using namespace std;int LongTimeCompute(int nNumber)
{// 模拟耗时操作this_thread::sleep_for(chrono::seconds(5));return nNumber * nNumber;
}int main()
{// 使用async启动异步任务auto future_result = async(launch::async, LongTimeCompute, 10);cout << "Main thread running..." << endl;// 当需要结果时,调用get()int nResult = future_result.get();cout << "Result of async computing: " << nResult << endl;return 0;
}
在上面的示例代码中,我们定义了一个模拟耗时计算的函数LongTimeCompute。然后,在main函数中,我们通过std::async创建了一个异步任务,并指定其策略为std::launch::async,确保在新的线程上运行该函数。
主线程在等待异步任务完成的同时可以继续执行其他任务,当主线程需要得到异步任务的结果时,它调用了 future_result.get(),这将阻塞直到异步计算完成并将结果返回给主线程。最后,主线程输出了异步计算得到的结果。
总结
C++ 11提供的并发特性不仅简化了多线程编程的复杂性,而且增强了程序的安全性和可靠性。通过合理利用这些工具和技术,我们能够更好地设计和实现适应现代多核架构的应用程序,从而提升软件的整体性能表现。在实践中,还需注意避免死锁、竞态条件等并发问题,并结合实际情况选择适当的并发策略。正确理解和熟练运用C++ 11的并发库,是构建高效、稳定且可扩展应用程序的关键。
相关文章:
C++ 11新特性之并发
概述 随着计算机硬件的发展,多核处理器已经成为主流,对程序并发执行能力的需求日益增长。C 11标准引入了一套全面且强大的并发编程支持库,为开发者提供了一个安全、高效地利用多核CPU资源进行并行计算的新框架,极大地简化了多线程…...

jvm问题自查思路
本文聊一下最近处理了一些jvm的问题上,将这个排查和学习过程分享一下,看了很多资料,最终都会落地到几个工具的使用,本文主要是从文档学习、工具学习和第三方技术验证来打开认知和实践,希望有用。 一、文档 不仅知道了…...
任意IOS16系统iPad/Iphone开启台前调度
方法来自GitHub: GitHub - khanhduytran0/TrollPad: Troll SpringBoard into thinking its running on iPadOS 注意操作前iPad/iPhone上需要安装巨魔手机助手和Filza,关于这两个软件的安装自行百度方法。 备注一个巨魔手机助手的下载地址 Release TrollStar 1.2…...

LeetCode、452. 用最少数量的箭引爆气球【中等,贪心,区间问题】
文章目录 前言LeetCode、452. 用最少数量的箭引爆气球【中等,贪心,区间问题】题目链接与分类思路贪心,连续区间数量问题 资料获取 前言 博主介绍:✌目前全网粉丝2W,csdn博客专家、Java领域优质创作者,博客…...
洛谷C++简单题小练习day10—umi的函数
day10--umi的函数--2.13 习题概述 题目背景 umi 找到了一个神秘的函数 f。 题目描述 这个函数接受两个字符串 s1,s2。这些字符串只能由小写字母组成并且具有相同的长度。这个函数的输出是另一个长度与 s1,s2 相同的字符串 g。 g 的第 i 个字符等于 s1 的第 i 个字符和 s2…...

【Linux学习】线程互斥与同步
目录 二十.线程互斥 20.1 什么是线程互斥? 20.2 为什么需要线程互斥? 20.3 互斥锁mutex 20.4 互斥量的接口 20.4.1 互斥量初始 20.4.2 互斥量销毁 20.4.3 互斥量加锁 20.4.4 互斥量解锁 20.4.5 互斥量的基本原理 20.4.6 带上互斥锁后的抢票程序 20.5 死锁问题 死锁…...
前端开发:(三)CSS入门
1. 介绍CSS 1.1 什么是CSS CSS(Cascading Style Sheets)是一种用于描述文档样式和布局的样式表语言,用于美化和排版HTML和XML等标记语言的内容。 1.2 CSS的作用和优势 CSS的主要作用是控制网页的样式和布局,包括字体、颜色、间…...

一周学会Django5 Python Web开发-Django5创建项目(用PyCharm工具)
锋哥原创的Python Web开发 Django5视频教程: 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计11条视频,包括:2024版 Django5 Python we…...
寒假学习记录13:JS对象
目录 对象转数组 对象双值转数组 Object.entries() (转为二维数组)(属性的值和键) 对象右值转数组 Object.values() (属性的值) 对象左值转数组 Object.keys() (属性的键) 对象左值转…...

学生成绩管理系统|基于Springboot的学生成绩管理系统设计与实现(源码+数据库+文档)
学生成绩管理系统目录 目录 基于Springboot的学生成绩管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员功能模块 2、学生功能模块 3、教师功能模块 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源…...

C#向数组指定索引位置插入新的元素值:自定义插入方法 vs List<T>.Add(T) 方法
目录 一、使用的方法 1.自定义插入方法 2.使用List.Add(T) 方法 二、实例 1.示例1:List.Add(T) 方法 2.示例:自定义插入方法 一、使用的方法 1.自定义插入方法 首先需要定义一个一维数组,然后修改数组的长度(这里使用Length属性获取…...

【大数据Hive】hive 表设计常用优化策略
目录 一、前言 二、hive 普通表查询原理 2.1 操作演示说明 2.1.1 创建一张表,并加载数据 2.1.2 统计3月24号的登录人数 2.1.3 查询原理过程总结 2.2 普通表结构带来的问题 三、hive分区表设计 3.1 区表结构 - 分区设计思想 3.2 操作演示 3.2.1 创建分区表…...

jvm垃圾收集器之七种武器
目录 1.回收算法 1.1 标记-清除算法(Mark-Sweep) 1.2 复制算法(Copying) 1.3 标记-整理算法(Mark-Compact) 2.HotSpot虚拟机的垃圾收集器 2.1 新生代的收集器 Serial 收集器(复制算法) ParNew 收集器 (复制算法) Parallel Scavenge 收集器 (复制…...
STM32面试相关问题
STM32面试相关问题: STM32的内核型号,主频,传感器和单片机总线类型,IIC,SPI,RS485UART数据帧项目中一些参数的设置 STM32 系统移植 ARM编译 常用的驱动编写方式 自己写过哪些方面驱动 其实如果问32的问题,…...
风行智能电视N39S、N40 强制刷机升级方法,附刷机升级数据MstarUpgrade.bin
升级步骤: 1、下载刷机数据,如是压缩包,需要先解压,然后将刷机bin格式的文件重命名为MstarUpgrade.bin 2、将此文件放到U盘根目录 (U盘格式FAT32,单分区,建议4G的优盘刷机成功率高)…...
【C语言】简易英语词典
文章目录 一、定义英语单词信息的结构体二、主函数功能逻辑三、查单词函数四、背单词函数五、补充 一、定义英语单词信息的结构体 添加必要的头文件、宏定义和声明,之后定义英语单词信息结构体。 /* 头文件和宏定义 */ #include <stdio.h> #include <std…...
【算法题】104. 二叉树的最大深度
题目 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:3 示例 2: 输入:root [1,nul…...

Docker配置Portainer容器管理界面
目录 一、Portainer 简介 优点: 缺点: 二、环境配置 1. 拉取镜像 2. 创建启动容器 三、操作测试 1. 进入容器 2. 拉取镜像并部署 3. 访问测试 一、Portainer 简介 Portainer 是一个开源的轻量级容器管理界面,用于管理 Docker 容器…...

Linux network namespace 访问外网以及多命名空间通信(经典容器组网 veth pair + bridge 模式认知)
写在前面 整理K8s网络相关笔记博文内容涉及 Linux network namespace 访问外网方案 Demo实际上也就是 经典容器组网 veth pair bridge 模式理解不足小伙伴帮忙指正 不必太纠结于当下,也不必太忧虑未来,当你经历过一些事情的时候,眼前的风景已…...

网络渗透测试:Wireshark抓取qq图片
Wireshark Wireshark Downloadhttps://www.wireshark.org/download.html 简介 WireShark是非常流行的网络封包分析工具,可以截取各种网络数据包,并显示数据包详细信息。常用于开发测试过程中各种问题定位。本文主要内容包括: 1、Wireshar…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...

密码学基础——SM4算法
博客主页:christine-rr-CSDN博客 专栏主页:密码学 📌 【今日更新】📌 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 编辑…...
python读取SQLite表个并生成pdf文件
代码用于创建含50列的SQLite数据库并插入500行随机浮点数据,随后读取数据,通过ReportLab生成横向PDF表格,包含格式化(两位小数)及表头、网格线等美观样式。 # 导入所需库 import sqlite3 # 用于操作…...