C++并发编程完全指南:从基础到实践

在当今多核处理器普及的时代,充分利用硬件并发能力已成为高性能编程的关键。C++11引入的现代并发编程支持使得开发者能够以标准化、可移植的方式编写多线程程序。本文将全面介绍C++并发编程的各个方面,从基础概念到实际应用,帮助您掌握这一重要技能。

一、并发编程概述

1.1 为什么需要并发编程

随着处理器核心数量的增加,单线程程序已经无法充分利用现代硬件资源。并发编程主要解决以下问题:

  • 性能提升:通过并行处理加速计算密集型任务

  • 响应性增强:保持用户界面响应同时执行后台操作

  • 资源利用:高效利用多核CPU和I/O等待时间

  • 任务分离:将复杂系统分解为独立协作的组件

1.2 并发与并行的区别

虽然经常混用,但这两个概念有本质区别:

  • 并发(Concurrency):多个任务交替执行,看似同时进行

  • 并行(Parallelism):多个任务真正同时执行,需要多核支持

C++标准库提供了同时支持这两种模式的统一接口。

二、线程基础

2.1 线程创建与管理

C++通过std::thread类提供线程支持,基本用法如下:

#include <iostream>
#include <thread>void thread_task() {std::cout << "Hello from thread " << std::this_thread::get_id() << "\n";
}int main() {std::thread t(thread_task);std::cout << "Main thread ID: " << std::this_thread::get_id() << "\n";t.join();return 0;
}

关键点:

  • 线程在构造时立即开始执行

  • 必须明确选择join()detach()

  • 主线程结束时会终止整个程序

2.2 线程参数传递

线程函数支持任意数量和类型的参数:

void print_sum(int a, int b) {std::cout << a + b << "\n";
}int main() {std::thread t(print_sum, 3, 4);t.join();
}

参数总是按值传递,如需传递引用必须使用std::ref包装:

void modify(int& x) {x *= 2;
}int main() {int value = 5;std::thread t(modify, std::ref(value));t.join();std::cout << value; // 输出10
}

2.3 线程生命周期管理

正确处理线程生命周期至关重要:

std::thread create_thread() {return std::thread([]{std::cout << "New thread\n";});
}int main() {std::thread t = create_thread();if (t.joinable()) {t.detach(); // 或t.join()}// 错误示例:线程未join或detach// std::thread t2([]{...});// t2的析构函数会调用std::terminate
}

三、同步机制

3.1 互斥量(Mutex)

互斥量是最基本的同步原语,C++提供多种变体:

类型特性
std::mutex基本互斥量
std::recursive_mutex可重入互斥量
std::timed_mutex支持超时锁定
std::shared_mutex读写锁(C++14)

基本用法:

std::mutex mtx;
int shared_data = 0;void increment() {mtx.lock();++shared_data;mtx.unlock();
}// 更安全的RAII方式
void safe_increment() {std::lock_guard<std::mutex> lock(mtx);++shared_data;
}

3.2 条件变量(Condition Variable)

条件变量允许线程等待特定条件成立:

std::mutex mtx;
std::condition_variable cv;
bool ready = false;
std::string data;void worker() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return ready; });std::cout << "Processing: " << data << "\n";
}int main() {std::thread t(worker);// 准备数据{std::lock_guard<std::mutex> lock(mtx);data = "Sample data";ready = true;}cv.notify_one();t.join();
}

3.3 原子操作

对于简单数据类型,原子操作比互斥量更高效:

#include <atomic>std::atomic<int> counter(0);void increment_atomic() {for (int i = 0; i < 1000; ++i) {++counter;}
}int main() {std::thread t1(increment_atomic);std::thread t2(increment_atomic);t1.join();t2.join();std::cout << counter; // 总是2000
}

四、高级并发特性

4.1 异步任务(std::async)

std::async提供更高层次的异步执行抽象:

#include <future>int compute() {std::this_thread::sleep_for(std::chrono::seconds(1));return 42;
}int main() {auto future = std::async(std::launch::async, compute);// 执行其他工作...std::cout << "Result: " << future.get() << "\n";
}

启动策略:

  • std::launch::async:立即异步执行

  • std::launch::deferred:延迟到get()/wait()时执行

4.2 并行算法(C++17)

C++17引入了并行标准算法:

#include <algorithm>
#include <vector>int main() {std::vector<int> data(1000000);// 并行排序std::sort(std::execution::par, data.begin(), data.end());// 并行变换std::transform(std::execution::par,data.begin(), data.end(), data.begin(),[](int x) { return x * 2; });
}

执行策略:

  • std::execution::seq:顺序执行

  • std::execution::par:并行执行

  • std::execution::par_unseq:并行且向量化

五、并发设计模式

5.1 生产者-消费者模式

#include <queue>std::mutex mtx;
std::condition_variable cv;
std::queue<int> msg_queue;void producer() {for (int i = 0; i < 10; ++i) {{std::lock_guard<std::mutex> lock(mtx);msg_queue.push(i);}cv.notify_one();std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}void consumer() {while (true) {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return !msg_queue.empty(); });int msg = msg_queue.front();msg_queue.pop();lock.unlock();if (msg == -1) break; // 终止信号std::cout << "Received: " << msg << "\n";}
}

5.2 线程池实现

基本线程池结构:

class ThreadPool {std::vector<std::thread> workers;std::queue<std::function<void()>> tasks;std::mutex queue_mutex;std::condition_variable condition;bool stop = false;public:ThreadPool(size_t threads) {for (size_t i = 0; i < threads; ++i) {workers.emplace_back([this] {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(queue_mutex);condition.wait(lock,[this]{ return stop || !tasks.empty(); });if (stop && tasks.empty()) return;task = std::move(tasks.front());tasks.pop();}task();}});}}template<class F>void enqueue(F&& f) {{std::unique_lock<std::mutex> lock(queue_mutex);tasks.emplace(std::forward<F>(f));}condition.notify_one();}~ThreadPool() {{std::unique_lock<std::mutex> lock(queue_mutex);stop = true;}condition.notify_all();for (std::thread &worker : workers)worker.join();}
};

六、性能考量与最佳实践

  1. 避免虚假共享:将频繁写入的变量放在不同缓存行

    struct alignas(64) CacheLineAligned {int data;
    };
  2. 锁粒度:保持锁的粒度尽可能小

  3. 无锁编程:在性能关键路径考虑无锁数据结构

  4. 线程数量:通常等于硬件线程数,可通过std::thread::hardware_concurrency()获取

  5. 异常安全:确保锁在异常情况下也能释放

  6. 死锁预防

    • 按固定顺序获取多个锁

    • 使用std::lock同时获取多个锁

    • 避免在持有锁时调用用户代码

七、C++20新增特性

  1. 信号量(Semaphore)

    #include <semaphore>
    std::counting_semaphore<10> sem(0);void worker() {sem.acquire();// 执行工作...
    }void post() {sem.release();
    }

  2. 闩(Latch)和屏障(Barrier)

    std::latch completion_latch(5); // 需要5次count_down()
    std::barrier sync_point(3);    // 每3个线程到达后继续
  3. 协程支持:为异步编程提供新范式

结语

C++并发编程是一个庞大而复杂的主题,本文涵盖了从基础线程操作到高级设计模式的广泛内容。掌握这些知识后,您将能够:

  1. 构建高效的多线程应用程序

  2. 设计线程安全的数据结构

  3. 避免常见的并发陷阱

  4. 充分利用现代硬件资源

记住,并发编程的首要目标是正确性,其次才是性能。始终优先考虑代码的安全性和可维护性,只有在必要时才进行复杂的优化。

随着C++标准的演进,并发支持仍在不断增强。建议持续关注新特性,如C++23中预计加入的更多并行算法和增强的协程支持。

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

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

相关文章

如何使用docker配置ros-noetic环境并使用rviz,gazebo

参考链接&#xff1a;【Ubuntu】Docker中配置ROS并可视化Rviz及Gazebo_docker ros-CSDN博客 前言&#xff1a; 其实这个东西是相当必要的&#xff0c;因为我们有时候需要在一台电脑上跑好几个项目&#xff0c;每个项目都有不同的依赖&#xff0c;这些依赖冲突搞得人头皮发麻&…

使用 Java 11 的 HttpClient 处理 RESTful Web 服务

在现代 Web 开发中,与 RESTful Web 服务交互是一项核心任务。Java 作为一种广泛使用的编程语言,提供了多种处理 HTTP 请求的方法。在 Java 11 之前,开发者通常使用 HttpURLConnection 或第三方库(如 Apache HttpClient)。然而,这些方法要么过于底层,要么需要额外依赖。J…

JVM——垃圾回收

垃圾回收 在Java虚拟机&#xff08;JVM&#xff09;的自动内存管理中&#xff0c;垃圾回收&#xff08;Garbage Collection, GC&#xff09;是其核心组件之一。它负责回收堆内存中不再使用的对象所占用的内存空间&#xff0c;以供新对象的分配使用。下面我们将深入探讨JVM中的…

经典密码学算法实现

# AES-128 加密算法的规范实现&#xff08;不使用外部库&#xff09; # ECB模式S_BOX [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B,0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0x…

追踪大型语言模型的思想(下)(来自针对Claude的分析)

多步推理 正如我们上面所讨论的&#xff0c;语言模型回答复杂问题的一种方式就是简单地记住答案。例如&#xff0c;如果问“达拉斯所在州的首府是哪里&#xff1f;”&#xff0c;一个“机械”的模型可以直接学会输出“奥斯汀”&#xff0c;而无需知道德克萨斯州&#xff0c;达拉…

【Hive入门】Hive增量数据导入:基于Sqoop的关系型数据库同步方案深度解析

目录 引言 1 增量数据导入概述 1.1 增量同步与全量同步对比 1.2 增量同步技术选型矩阵 2 Sqoop增量导入原理剖析 2.1 Sqoop架构设计 2.2 增量同步核心机制 3 Sqoop增量模式详解 3.1 append模式&#xff08;基于自增ID&#xff09; 3.2 lastmodified模式&#xff08;基…

[Windows] 蓝山看图王 1.0.3.21021

[Windows] 蓝山看图王 链接&#xff1a;https://pan.xunlei.com/s/VOPejo0dRLFd38dbpR7XA6djA1?pwddq9v# 由蓝山软件出品的一款免费高清看图软件&#xff0c;支持快速查看高清大图&#xff0c;支持大部分市面图片格式。 软件特点 1、体积小巧&#xff0c;图像显示效果清晰…

通配符 DNS 记录:应用场景与相关风险

随着组织的互联网基础设施不断扩展&#xff0c;其对配置、设置和决策的需求也随之增加——从选择一个可靠的名称服务器&#xff0c;到确定合适的 DNS 记录类型以及设置合适的 TTL&#xff08;生存时间&#xff09;值。其中一项关键决策就是是否要创建通配符 DNS 记录&#xff0…

快速上手知识图谱开源库pykeen教程指南(一)

文章目录 1 前情提要1.1 AmpliGraph 和 PyKEEN 对比介绍1.2 TransE、ConvE、RotatE几款模型的差异 2 直接上案例2.1 数据载入&#xff1a;TriplesFactory2.2 模型训练2.2.1 训练信息2.2.2 TransE模型可以降维实体、关系的关系 2.3 模型保存与加载、评估2.3.1 保存与模型加载2.3…

飞搭系列 | 获取弹窗数据,轻松实现回填

前言 飞搭低代码平台&#xff08;FeiDa&#xff0c;以下简称“飞搭”&#xff09;&#xff0c;为企业提供在线化、灵活的业务应用构建工具&#xff0c;支持高低代码融合&#xff0c;助力企业低门槛、高效率和低成本地快速应对市场变化&#xff0c;加速复杂业务场景落地。 概要…

Linux如何安装AppImage程序

Linux如何安装AppImage程序 文章目录 Linux如何安装AppImage程序 在 Linux 中&#xff0c;.AppImage 是一种便携式的应用程序格式&#xff0c;无需安装即可运行。 1.赋予该文件可执行权限 可以使用下列命令&#xff0c;赋予可执行权限 # 举个例子 chmod x /path/to/MyApp.App…

云硬盘的原理

云硬盘是云计算环境中的一种存储服务&#xff0c;其原理主要涉及数据存储、数据冗余与容错、性能优化以及数据安全等方面&#xff0c;以下是具体介绍&#xff1a; 数据存储 逻辑卷管理&#xff1a;云硬盘通常会将物理存储设备划分为多个逻辑卷&#xff0c;每个逻辑卷可以独立地…

使用 pgrep 杀掉所有指定进程

使用 pgrep 杀掉所有指定进程 pgrep 是一个查找进程 ID 的工具&#xff0c;结合 pkill 或 kill 命令可以方便地终止指定进程。以下是几种方法&#xff1a; 方法1&#xff1a;使用 pkill&#xff08;最简单&#xff09; pkill 进程名例如杀掉所有名为 “firefox” 的进程&…

堆排序(算法题)

#include <bits/stdc.h> using namespace std;const int N 100010; // 堆数组的最大容量 int h[N], s; // h[]存储堆元素&#xff0c;s表示当前堆的大小// 下沉操作&#xff1a;调整以i为根的子树&#xff0c;维护小顶堆性质 void down(int i) {int t i; /…

极狐GitLab 如何将项目共享给群组?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 共享项目和群组 (BASIC ALL) 在极狐GitLab 16.10 中&#xff0c;更改为在成员页面的成员选项卡上显示被邀请群组成员&#xf…

用 CodyBuddy 帮我写自动化运维脚本

我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴”。 #CodeBuddy首席试玩官 背景 我个人是非常喜欢 Jenkins 自动化部署工具的&#xff0c;之前都是手写 Jenki…

基于windows安装MySQL8.0.40

基于windows安装MySQL8.0.40 基于windows 安装 MySQL8.0.40&#xff0c;解压文件到D:\mysql-8.0.40-winx64 在D:\mysql-8.0.40-winx64目录下创建my.ini文件&#xff0c;并更新一下内容 [client] #客户端设置&#xff0c;即客户端默认的连接参数 # 设置mysql客户端连接服务…

Python小酷库系列:5个常用的dict属性化访问扩展库

5个常用的dict属性化访问扩展库 嵌套结构高级功能性能综合建议 在前面我们详细讲解了 Box和 Munch这两个dict属性化访问的扩展库&#xff0c;总体而言它们主要用于提升配置文件数据、JSON对象数据的可读性&#xff0c;减少了代码中双引号。在这一领域中还有dotmap、addict 和…

OC语言学习——面向对象(下)

一、OC的包装类 OC提供了NSValue、NSNumber来封装C语言基本类型&#xff08;short、int、float等&#xff09;。 在 Objective-C 中&#xff0c;**包装类&#xff08;Wrapper Classes&#xff09;**是用来把基本数据类型&#xff08;如 int、float、char 等&#xff09;“包装…

密码学系列 - SR25519与ED25519

SR25519 SR25519 是一种高级的数字签名算法&#xff0c;它基于 Schnorr 签名方案&#xff0c;使用的是 Curve25519 椭圆曲线。这种签名算法在密码学社区中广受欢迎&#xff0c;特别是在区块链和加密货币领域。以下是关于 SR25519 的详细介绍。 SR25519 简介 SR25519 是一种 …