本部分内容属于:
C++并发编程系列-基于标准库
第一部分:并发编程基础与C++线程模型
1.2 C++的并发编程演进
1.2.1 C++11/14/17 并发特性迭代
其它章节内容请查看对应章节。
1.2.1 C++11/14/17 并发特性迭代
在C++11之前,C++标准完全没有对并发编程提供原生支持,开发者只能依赖平台特定的API(如Windows的CreateThread、POSIX的pthread)实现多线程,代码的可移植性和安全性极差。C++11首次将并发编程纳入标准库,奠定了跨平台并发的基础;C++14对已有特性进行了轻量级增强,提升易用性;C++17则完善了核心组件,新增了实用工具,让标准并发库更完整、更高效。
一、C++11:并发编程的"开山之作"
C++11是C++并发编程的里程碑,核心目标是提供跨平台的线程抽象和基础同步原语,解决此前依赖平台API的移植性问题。其核心并发特性可分为三类:
1. 线程基础抽象
-
std::thread:操作系统线程的标准封装,支持创建、启动、等待(join)、分离(detach)线程,替代平台原生线程创建接口。 -
std::this_thread命名空间:提供线程级操作(sleep_for、yield、get_id),统一不同平台的线程控制逻辑。 -
std::thread_local:线程本地存储(TLS),保证变量在每个线程有独立副本,避免全局变量的线程安全问题。
2. 同步原语
-
基础互斥量:
std::mutex、std::recursive_mutex、std::timed_mutex、std::recursive_timed_mutex,解决共享数据竞争。 -
RAII锁管理器:
std::lock_guard(轻量级独占锁)、std::unique_lock(灵活独占锁),避免手动解锁导致的资源泄漏。 -
条件变量:
std::condition_variable、std::condition_variable_any,实现线程间的等待-通知通信。 -
原子操作:
std::atomic模板及内存序(memory_order_*),支持无锁的原子读写,替代部分场景下的互斥量。
3. 异步操作框架
-
std::future/std::promise:实现线程间的结果传递,解决"生产者-消费者"模型中结果同步的问题。 -
std::packaged_task:包装可调用对象为异步任务,关联std::future获取结果。 -
std::async:简化异步任务创建,自动管理底层线程(支持launch::async/launch::deferred两种策略)。
C++11核心并发特性示例:基础线程与互斥量
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
// 全局共享变量
int shared_counter = 0;
// 全局互斥量,保护共享变量
std::mutex counter_mutex;
// 线程函数:对共享变量累加
void increment_counter(int iterations) {
for (int i = 0; i < iterations; ++i) {
// RAII锁:构造时加锁,析构时解锁,避免死锁
std::lock_guard<std::mutex> lock(counter_mutex);
shared_counter++;
// 模拟耗时操作,让出CPU(C++11 std::this_thread::yield)
std::this_thread::yield();
}
}
int main() {
// 创建2个线程,每个线程累加10000次
std::vector<std::thread> threads;
threads.emplace_back(increment_counter, 10000);
threads.emplace_back(increment_counter, 10000);
// 等待所有线程完成(C++11 std::thread::join)
for (auto& t : threads) {
if (t.joinable()) {
t.join();
}
}
// 输出最终结果(预期为20000)
std::cout << "Final counter value: " << shared_counter << std::endl;
return 0;
}
代码说明:
-
核心组件:
-
std::mutex:保护shared_counter避免数据竞争,确保每次只有一个线程修改该变量; -
std::lock_guard:RAII封装互斥量,即使increment_counter抛出异常,析构时也会自动解锁; -
std::thread:创建线程对象,emplace_back直接构造线程(C++11移动语义支持); -
std::this_thread::yield:让出CPU时间片,模拟实际场景中的线程切换。
-
-
编译与运行:
-
编译命令(GCC/Clang):
g++ -std=c++11 thread_demo.cpp -o thread_demo -pthread; -
运行结果:
Final counter value: 20000(若无互斥量,结果会随机小于20000,体现数据竞争)。
-
二、C++14:轻量级增强,提升易用性
C++14并未新增核心并发组件,而是针对C++11的已有特性做易用性优化,降低开发门槛:
1. 原子操作增强
-
支持对
std::atomic<T>的泛型lambda操作,简化原子类型的自定义逻辑; -
允许
std::atomic对更多类型(如自定义POD类型)的隐式推导,减少代码冗余。
2. 异步操作优化
-
std::shared_future支持移动和拷贝的更灵活语义,允许多个线程共享同一个异步结果; -
修复
std::async在部分编译器中的行为不一致问题(如launch::async策略的线程创建时机); -
std::condition_variable_any支持更多自定义锁类型,不再局限于标准互斥量。
3. 语法糖简化并发代码
- 泛型lambda配合
std::thread,简化线程函数的定义(无需单独写函数,直接内联lambda)。
C++14增强特性示例:泛型lambda+原子操作
cpp
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
int main() {
// C++11已支持,但C++14泛型lambda让用法更灵活
std::atomic<int> sum(0);
const int thread_count = 4;
const int iterations = 1000;
// C++14泛型lambda作为线程函数
auto add_task = [&sum](int start, int end) {
for (int i = start; i < end; ++i) {
// C++11原子操作,C++14无语法变化,但lambda泛型简化调用
sum.fetch_add(i, std::memory_order_relaxed);
}
};
std::vector<std::thread> threads;
for (int i = 0; i < thread_count; ++i) {
int start = i * iterations;
int end = (i + 1) * iterations;
threads.emplace_back(add_task, start, end);
}
// 等待所有线程完成
for (auto& t : threads) {
t.join();
}
// 计算预期结果:0+1+...+3999 = (3999*4000)/2 = 7998000
std::cout << "Atomic sum result: " << sum.load() << std::endl;
return 0;
}
代码说明:
-
C++14核心优化点:
-
泛型lambda
add_task无需显式指定参数类型(int start, int end),编译器自动推导,简化线程函数编写; -
std::atomic::fetch_add的memory_order_relaxed(松散内存序)在C++11已支持,但C++14让原子操作与泛型结合更自然。
-
-
编译与运行:
-
编译命令:
g++ -std=c++14 atomic_demo.cpp -o atomic_demo -pthread; -
运行结果:
Atomic sum result: 7998000(原子操作保证结果准确,无数据竞争)。
-
三、C++17:完善核心组件,新增实用工具
C++17是C++11并发模型的"补全版",解决了C++11/14中部分组件的痛点,新增了高价值工具,核心改进如下:
1. 新增核心同步组件
-
std::scoped_lock:替代std::lock + std::lock_guard,支持多互斥量无死锁加锁,且是类型安全的RAII锁; -
std::shared_mutex/std::shared_timed_mutex:正式纳入标准的读写锁(C++14仅为实验性),实现"多读单写"的高效同步; -
std::shared_lock:配合std::shared_mutex使用的共享锁,支持多个读线程同时持有锁,写线程独占锁。
2. 并行算法(std::execution)
-
新增
std::execution命名空间,提供三种执行策略:-
std::execution::seq:串行执行(默认); -
std::execution::par:并行执行(多线程); -
std::execution::par_unseq:并行+向量化执行(更激进的并行优化);
-
-
标准库算法(
std::sort、std::for_each、std::find等)支持传入执行策略,自动实现并行化,无需手动创建线程。
3. 其他优化
-
std::invoke:统一可调用对象的调用方式(函数、成员函数、lambda、函数对象),简化线程函数的适配; -
std::byte:与原子操作兼容,支持更安全的内存字节级操作; -
修复
std::thread在部分平台的兼容性问题,明确joinable()的状态判断规则。
C++17核心新增特性示例:std::scoped_lock + 并行排序
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <shared_mutex>
#include <algorithm>
#include <vector>
#include <execution> // C++17并行算法头文件
// 读写锁示例:多读单写
class SharedData {
private:
std::vector<int> data;
// C++17正式标准的读写锁
std::shared_mutex rw_mutex;
public:
// 写操作:独占锁
void add_data(int value) {
// 独占锁:写线程独占,阻塞所有读/写线程
std::lock_guard<std::shared_mutex> lock(rw_mutex);
data.push_back(value);
std::cout << "Write thread " << std::this_thread::get_id() << " added: " << value << std::endl;
}
// 读操作:共享锁
void print_data() {
// 共享锁:多个读线程可同时持有,阻塞写线程
std::shared_lock<std::shared_mutex> lock(rw_mutex);
std::cout << "Read thread " << std::this_thread::get_id() << " data size: " << data.size() << std::endl;
}
// C++17并行排序:自动多线程排序
void sort_data_parallel() {
// 独占锁:排序时禁止读/写
std::scoped_lock lock(rw_mutex); // C++17 scoped_lock替代lock_guard
// 并行执行策略:自动多线程排序
std::sort(std::execution::par, data.begin(), data.end());
std::cout << "Sort thread " << std::this_thread::get_id() << " sorted data" << std::endl;
}
};
int main() {
SharedData sd;
// 1. 多个写线程添加数据
std::vector<std::thread> write_threads;
for (int i = 0; i < 5; ++i) {
write_threads.emplace_back(&SharedData::add_data, &sd, i * 10);
}
for (auto& t : write_threads) {
t.join();
}
// 2. 多个读线程读取数据
std::vector<std::thread> read_threads;
for (int i = 0; i < 3; ++i) {
read_threads.emplace_back(&SharedData::print_data, &sd);
}
for (auto& t : read_threads) {
t.join();
}
// 3. 并行排序数据
std::thread sort_thread(&SharedData::sort_data_parallel, &sd);
sort_thread.join();
return 0;
}
代码说明:
-
C++17核心新增点:
-
std::shared_mutex:正式标准的读写锁,add_data(写)用lock_guard独占,print_data(读)用shared_lock共享,提升读并发效率; -
std::scoped_lock:替代std::lock_guard,语法更简洁,且支持多互斥量(如std::scoped_lock lock(m1, m2)),自动避免死锁; -
std::execution::par:并行排序策略,std::sort自动创建线程完成排序,无需手动管理线程池。
-
-
编译与运行:
-
编译命令:
g++ -std=c++17 shared_mutex_demo.cpp -o shared_mutex_demo -pthread; -
运行结果示例(线程ID随机):
PlainWrite thread 140709284798272 added: 0 Write thread 140709276405568 added: 10 Write thread 140709268012864 added: 20 Write thread 140709259620160 added: 30 Write thread 140709251227456 added: 40 Read thread 140709242834752 data size: 5 Read thread 140709234442048 data size: 5 Read thread 140709226049344 data size: 5 Sort thread 140709217656640 sorted data
-
四、C++11/14/17 并发特性迭代总结表
| 版本 | 核心新增/优化 | 解决的核心问题 |
|---|---|---|
| C++11 | 1. std::thread/互斥量/条件变量 2. std::atomic/内存序 3. std::future/promise/async | 无标准并发库,依赖平台API,移植性差 |
| C++14 | 1. 泛型lambda适配并发代码 2. std::shared_future增强 3. 原子操作泛型支持 | C++11并发代码冗余,lambda使用不灵活 |
| C++17 | 1. std::scoped_lock(多互斥量无死锁) 2. std::shared_mutex(读写锁) 3. 并行算法(std::execution) | C++11/14同步方式单一,手动并行开发成本高 |
总结
-
C++11是并发编程的基础,首次提供跨平台的线程、同步、异步核心组件,解决了平台依赖问题;
-
C++14以"易用性优化"为主,通过泛型lambda等语法糖简化并发代码编写,无核心组件新增;
-
C++17完善了同步组件(读写锁、scoped_lock),新增并行算法,大幅降低并行开发成本,是C++11并发模型的重要补全。
这些迭代的核心逻辑是:从"能实现并发"(C++11)→"易实现并发"(C++14)→"高效实现并发"(C++17),逐步降低开发门槛,提升并发代码的安全性和性能。