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…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...
《Offer来了:Java面试核心知识点精讲》大纲
文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...