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的并发库,是构建高效、稳定且可扩展应用程序的关键。

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

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

相关文章

C#面:Static Nested Class 和 Inner Class 有什么不同

这是两种不同的类嵌套方式。 Static Nested Class &#xff1a; 是一个静态嵌套类&#xff0c;它是在外部类中定义的一个静态类。它可以访问外部类的静态成员和方法&#xff0c;但不能直接访问外部类的非静态成员和方法。静态嵌套类可以独立于外部类实例化&#xff0c;即可以…

《Linux 简易速速上手小册》第6章: 磁盘管理与文件系统(2024 最新版)

文章目录 6.1 磁盘分区与格式化6.1.1 重点基础知识6.1.2 重点案例&#xff1a;为新硬盘配置分区和文件系统6.1.3 拓展案例 1&#xff1a;创建交换分区6.1.4 拓展案例 2&#xff1a;使用 LVM 管理分区 6.2 挂载与卸载文件系统6.2.1 重点基础知识6.2.2 重点案例&#xff1a;挂载新…

近十年金融资产收益率

通过掌握大类资产的历年收益率数据&#xff0c;做基于数据的投资&#xff0c;提高胜率和收益率。 下面是同花顺梳理的2014至2023大类金融资产收益率&#xff1a; 基于这个数据&#xff0c;我们再统计两项指标&#xff1a; 1. 每种资产在近十年的投资胜率&#xff08;收益率为…

牛客2024年情人节比赛 娱乐报告

前言 挺欢乐的比赛&#xff0c;有趣 欢迎关注 珂朵莉 牛客周赛专栏 珂朵莉 牛客小白月赛专栏 A. 第二杯半价 思路: 模拟 分奇偶进行讨论 t int(input())for _ in range(t):n, x list(map(int, input().split()))if n % 2 1:print (n//2 * (x (x 1) // 2) x)else:pr…

Fabric自动化部署

Fabric自动化部署是一种基于Python的自动化部署工具&#xff0c;它可以帮助开发人员自动化地执行一系列部署任务&#xff0c;如代码推送、服务器配置更新、文件传输等。 Fabric自动化部署的核心是一个名为fabfile.py的Python文件&#xff0c;其中定义了要执行的部署任务。通过…

力扣-345. 反转字符串中的元音字母

文章目录 力扣题目代码 力扣题目 给你一个字符串 s &#xff0c;仅反转字符串中的所有元音字母&#xff0c;并返回结果字符串。 元音字母包括 ‘a’、‘e’、‘i’、‘o’、‘u’&#xff0c;且可能以大小写两种形式出现不止一次。 示例 1&#xff1a; 输入&#xff1a;s …

RIDERS: Radar-Infrared Depth Estimation for Robust Sensing

RIDERS: 恶劣天气及环境下鲁棒的密集深度估计 论文链接&#xff1a;https://arxiv.org/pdf/2402.02067.pdf 作者单位&#xff1a;浙江大学, 慕尼黑工业大学 代码链接&#xff1a;https://github.com/MMOCKING/RIDERS 1. 摘要&#xff08;Abstract&#xff09; 恶劣的天气条件, …

Ubuntu+Anaconda 常用指令记录

Anaconda 使用指令记录 1 创建环境 conda create -n name pythonx.x(python版本自己指定)例如 conda create --name myenv: 创建名为"myenv"的新环境。 conda activate myenv: 激活名为"myenv"的环境。 conda deactivate: 退出当前环境。 2 删除环境 c…

python 自我检测题--part 1

1. Which way among them is used to create an event loop ? Window.mainloop() 2. Suppose we have a set a {10,9,8,7}, and we execute a.remove(14) what will happen ? Key error is raised. The remove() method removes the specified element from the set. Th…

imazing怎么连接苹果手机

imazing怎么连接苹果手机 要连接苹果手机&#xff0c;您可以选择使用数据线或无线网络&#xff08;Wi-Fi&#xff09;两种方式。以下是具体的步骤&#xff1a; 使用数据线连接&#xff1a; 准备工具&#xff1a;确保您的Mac或Windows电脑已经安装了iMazing软件&#xff0c;并且…

流畅的Python(九)-符合Python风格的对象

一、核心要义 接续第一章&#xff0c;说明如何实现很多Python类型中常见的特殊方法 1. 支持使用生成对象其他表示形式的内置函数(如repr(),bytes()等) 2. 使用类方法&#xff0c;实现备选构造方法 3.扩展内置的format()函数和str.format()方法使用的格式微语言 4.实现只读…

寒假作业2024.2.14

一&#xff0e;选择题 1.变量的指针&#xff0c;其含义是指该变量的 B 。 A&#xff09;值 B&#xff09;地址 C&#xff09;名 D&#xff09;一个标志 2.已有定义int k2;int *ptr1,*ptr2;且ptr1和ptr2均已指向变量k&#xff…

Linux命令行全景指南:从入门到实践,掌握命令行的力量

目录 知识梳理思维导图&#xff1a; linux命令入门 为什么要学Linux命令 什么是终端 什么是命令 关于Linux命令的语法 tab键补全 关于命令提示符 特殊目录 常见重要目录 /opt /home /root /etc /var/log/ man命令 shutdown命令 history命令 which命令 bash…

顾问聘请协议(模板)

甲方&#xff1a;________________   乙方&#xff1a;________________ 诚信合作是一切事业发展的基础&#xff0c;外部智力是企业进步的源泉。甲、乙双方经友好协商达成本协议&#xff0c;甲方愿意聘请乙方为特邀管理顾问&#xff0c;乙方愿按本协议内容与甲方合作。 一、合…

PostgreSQL的学习心得和知识总结(一百二十九)|深入理解PostgreSQL数据库GUC参数 update_process_title 的使用和原理

目录结构 注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下: 1、参考书籍:《PostgreSQL数据库内核分析》 2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》 3、PostgreSQL数据库仓库链接,点击前往 4、日本著名PostgreSQL数据库专家 铃木启修 网站…

水果FL Studio21.2最新中文版功能特点介绍

FL Studio 21的特点和优势包括&#xff1a; 丰富的主题换肤&#xff1a;用户可以通过调整色调、饱和度、亮度、文本、仪表和步进序列器的颜色&#xff0c;来个性化定制FL Studio 21的外观&#xff0c;使其更符合个人审美或工作风格。更快的音频编辑&#xff1a;FL Studio 21集…

奇异递归模板模式应用1-对象计数

需求&#xff1a;有时遇到某些类特征相似而又没有共同的父类&#xff0c;希望能够知道这些类的创建数量之和。 思路&#xff1a;将这些类继承自同一个计数类&#xff0c;共享计数变量s_createCount信息&#xff0c;实现如下&#xff1a; class Counter { public:Counter() {s_…

Codeforces Round 925 (Div. 3)

D. Divisible Pairs 题意&#xff1a;给定一个长度为n(2<n<2*10^5)的数组&#xff0c;给出两个数x、y(1<x,y<10^9),找出完美对的个数 完美对满足 (aiaj)整除x (ai-aj)整除y 且&#xff08;1<i<j<n) 统计数组a中的完美对有多少个 思路&#xff1a;统计…

【2024年毕设系列】如何使用Anaconda和Pycharm

【2024年毕设系列】如何使用Anaconda和Pycharm 视频教程地址&#xff1a;【2024毕设系列】Anaconda和Pycharm如何使用_哔哩哔哩 Hi&#xff0c;各位好久不见&#xff0c;这里是肆十二&#xff0c;首先在这里给大伙拜年了。 诸位过完年之后估计又要开始为了大作业和毕业设计头疼…

Virt a Mate(VAM)游戏折腾记录

如有更新见原文&#xff1a;https://blog.iyatt.com/?p13283 1 前言 如果在网上看到有些视频名字带有 VAM 的&#xff0c;可能就是玩这个游戏录屏的。这个游戏可以建模、操作模型动作、构建场景等等。之前大致知道有这么个东西&#xff0c;只是电脑配置太差了&#xff0c;新…