C++ 线程库完全指南:从基础到实战
前言
在多核处理器普及的今天,多线程编程已经成为提升程序性能的必备技能。C++11 标准终于将线程支持纳入了标准库,结束了长期以来依赖平台特定API(如Windows的CreateThread、Linux的pthread)的历史。
标准线程库(<thread>)提供了跨平台的线程创建、管理和同步机制,配合<mutex>、<condition_variable>、<future>等头文件,构成了完整的并发编程基础。本文将从最基础的概念开始,带你全面掌握C++线程库的使用。
C++ 线程库主要包含在 <thread>、<mutex>、<condition_variable>、<future> 等头文件中。以下是核心函数和类的总结:
线程库函数列表
1. 线程管理 (<thread>)
| 函数/类 | 说明 |
|---|---|
std::thread |
线程类 |
thread::join() |
等待线程完成 |
thread::detach() |
分离线程(后台运行) |
thread::joinable() |
检查是否可 join |
std::this_thread::get_id() |
获取当前线程 ID |
std::this_thread::sleep_for(duration) |
当前线程睡眠一段时间 |
std::this_thread::sleep_until(time_point) |
睡眠到指定时间点 |
std::this_thread::yield() |
让出 CPU 时间片 |
std::hardware_concurrency() |
获取硬件并发线程数 |
cpp
#include <thread>
std::thread t([](){ /* work */ });
t.join(); // 或 t.detach()
2. 互斥量 (<mutex>)
| 类/函数 | 说明 |
|---|---|
std::mutex |
基本互斥量 |
std::recursive_mutex |
可递归锁 |
std::timed_mutex |
带超时的互斥量 |
std::recursive_timed_mutex |
可递归+超时 |
std::shared_mutex (C++17) |
读写锁 |
mutex::lock() |
加锁 |
mutex::unlock() |
解锁 |
mutex::try_lock() |
尝试加锁(非阻塞) |
RAII 锁管理
| 类 | 说明 |
|---|---|
std::lock_guard |
简单 RAII 锁 |
std::unique_lock |
灵活 RAII 锁(支持延迟、超时等) |
std::shared_lock (C++14) |
RAII 共享锁 |
std::scoped_lock (C++17) |
同时锁定多个互斥量(避免死锁) |
cpp
std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx); // 自动加/解锁
3. 条件变量 (<condition_variable>)
| 函数 | 说明 |
|---|---|
condition_variable::wait(lock, pred) |
等待(阻塞直到被通知且谓词为真) |
condition_variable::wait_for() |
带超时的等待 |
condition_variable::wait_until() |
等待到指定时间点 |
condition_variable::notify_one() |
唤醒一个等待线程 |
condition_variable::notify_all() |
唤醒所有等待线程 |
cpp
std::mutex mtx;
std::condition_variable cv;
std::unique_lock lk(mtx);
cv.wait(lk, []{ return ready; }); // 等待条件
cv.notify_one(); // 通知
4. 异步任务 (<future>)
| 类/函数 | 说明 |
|---|---|
std::async |
异步执行函数,返回 future |
std::future<T> |
获取异步结果 |
std::shared_future<T> |
可多次获取的 future |
std::promise<T> |
设置异步值 |
std::packaged_task |
包装可调用对象 |
future::get() |
获取结果(阻塞) |
future::wait() |
等待结果 |
future::wait_for() / wait_until() |
带超时的等待 |
cpp
std::future<int> fut = std::async([](){ return 42; });
int result = fut.get(); // 阻塞获取
5. 原子操作 (<atomic>)
| 类型/函数 | 说明 |
|---|---|
std::atomic<T> |
原子类型 |
std::atomic_flag |
最小原子布尔锁 |
std::atomic::load() / store() |
原子读/写 |
std::atomic::exchange() |
原子交换 |
std::atomic::compare_exchange_weak/strong() |
CAS 操作 |
std::atomic::fetch_add() 等 |
原子算术 |
cpp
std::atomic<int> counter{0};
counter.fetch_add(1, std::memory_order_relaxed);
6. 常用辅助函数
| 函数 | 说明 |
|---|---|
std::lock(m1, m2, ...) |
同时锁定多个互斥量(避免死锁) |
std::try_lock(m1, m2, ...) |
尝试锁定多个 |
std::call_once(flag, func) |
仅执行一次 |
线程管理 (<thread>) 详解
1. std::thread 类
线程类的核心,表示一个可执行的线程。
构造函数:
cpp
std::thread t; // 默认构造,不表示任何线程
std::thread t(callable, args...); // 创建线程并执行 callable
std::thread t(std::move(other)); // 移动构造,转移线程所有权
基本使用:
cpp
#include <thread>
#include <iostream>
void func(int x) {
std::cout << "Value: " << x << std::endl;
}
int main() {
std::thread t1(func, 42); // 创建线程执行 func(42)
std::thread t2([](){ // lambda 表达式
std::cout << "Lambda thread" << std::endl;
});
// 注意:必须 join 或 detach,否则析构时程序崩溃
t1.join();
t2.join();
}
2. thread::join()
作用:阻塞当前线程,等待目标线程执行完成。
关键点:
- 每个线程只能
join()一次 - 必须在
join()前调用joinable()检查 join()后线程对象不再关联任何线程
cpp
#include <thread>
#include <chrono>
#include <iostream>
void slow_task() {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Task completed" << std::endl;
}
int main() {
std::thread t(slow_task);
std::cout << "Waiting for thread..." << std::endl;
t.join(); // 主线程阻塞,直到 slow_task 完成
std::cout << "Thread finished" << std::endl;
// t.join(); // 错误!不能重复 join
return 0;
}
输出:
Waiting for thread...
(等待2秒)
Task completed
Thread finished
3. thread::detach()
作用 :将线程分离,使其在后台独立运行,不再能被 join()。
关键点:
- 分离后线程对象与线程执行分离
- 主线程不会等待分离的线程
- 线程会在执行完成后自动回收资源
- 分离后不能再
join()或重新detach()
cpp
#include <thread>
#include <chrono>
#include <iostream>
void background_task() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Background work done" << std::endl;
}
int main() {
std::thread t(background_task);
t.detach(); // 线程分离,独立运行
std::cout << "Main thread continues..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待后台线程完成
// t.join(); // 错误!detach 后不能再 join
// t.detach(); // 错误!不能重复 detach
return 0;
}
输出:
Main thread continues...
(1秒后)
Background work done
⚠️ 警告:如果主线程提前结束,分离的线程可能被强制终止:
cpp
int main() {
std::thread t([]{
std::this_thread::sleep_for(std::chrono::seconds(10));
std::cout << "Never printed" << std::endl;
});
t.detach();
return 0; // 主线程立即结束,后台线程被强制终止
}
4. thread::joinable()
作用:检查线程对象是否关联了一个可执行的线程。
返回 true 的情况:
- 线程被创建但未
join()或detach() - 移动构造后,源线程变为不可
joinable
返回 false 的情况:
- 默认构造的线程对象
- 已经
join()过 - 已经
detach()过 - 被移动后的源线程
cpp
#include <thread>
#include <iostream>
int main() {
std::thread t1; // 默认构造
std::thread t2([]{}); // 有线程关联
std::thread t3([]{}); // 有线程关联
std::cout << "t1 joinable: " << t1.joinable() << std::endl; // 0 (false)
std::cout << "t2 joinable: " << t2.joinable() << std::endl; // 1 (true)
t2.join(); // 等待完成
std::cout << "After join, t2 joinable: " << t2.joinable() << std::endl; // 0
t3.detach(); // 分离
std::cout << "After detach, t3 joinable: " << t3.joinable() << std::endl; // 0
return 0;
}
最佳实践:
cpp
if (t.joinable()) {
t.join(); // 安全地 join
}
5. std::this_thread::get_id()
作用:获取当前线程的唯一标识符。
cpp
#include <thread>
#include <iostream>
int main() {
std::cout << "Main thread ID: " << std::this_thread::get_id() << std::endl;
std::thread t([]{
std::cout << "Worker thread ID: " << std::this_thread::get_id() << std::endl;
std::cout << "Is main? " << (std::this_thread::get_id() == std::thread::id()) << std::endl;
});
std::cout << "Worker thread object ID: " << t.get_id() << std::endl;
t.join();
// 默认构造的 thread ID
std::thread empty;
std::cout << "Empty thread ID: " << empty.get_id() << std::endl; // 0 或特殊值
}
输出示例:
Main thread ID: 140736512397120
Worker thread object ID: 140736512397120
Worker thread ID: 123145476907008
Is main? 0
Empty thread ID: thread::id of a non-executing thread
用途:
- 调试和日志记录
- 检查是否在主线程
- 线程局部存储索引
6. std::this_thread::sleep_for(duration)
作用:阻塞当前线程至少指定的时长。
cpp
#include <thread>
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono_literals; // C++14,允许使用 1s, 500ms 等
std::cout << "Start" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 500ms
std::cout << "After 0.5s" << std::endl;
std::this_thread::sleep_for(2s); // C++14 语法
std::cout << "After 2s" << std::endl;
std::this_thread::sleep_for(100ms); // 100ms
std::cout << "Done" << std::endl;
}
注意事项:
- 可能睡眠时间比指定值略长(取决于系统调度)
- 适合轮询检查、节流控制等场景
实际应用:
cpp
// 带延迟的重试循环
for (int retry = 0; retry < 5; ++retry) {
if (try_operation()) break;
std::this_thread::sleep_for(std::chrono::seconds(1 << retry)); // 指数退避
}
7. std::this_thread::sleep_until(time_point)
作用:阻塞当前线程直到指定的时间点。
cpp
#include <thread>
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
auto start = steady_clock::now();
// 精确睡眠到 1 秒后
auto wake_time = start + seconds(1);
std::this_thread::sleep_until(wake_time);
auto elapsed = duration_cast<milliseconds>(steady_clock::now() - start);
std::cout << "Slept for " << elapsed.count() << "ms" << std::endl;
// 应用到特定时间点(如整秒执行)
auto next_execution = system_clock::now() + seconds(5);
std::this_thread::sleep_until(next_execution);
}
与 sleep_for 的区别:
sleep_for:相对时间,"睡眠 X 时长"sleep_until:绝对时间,"睡眠到时间点 T"
实际应用:
cpp
// 定时任务:每小时整点执行
void hourly_task() {
while (true) {
auto now = std::chrono::system_clock::now();
auto next_hour = std::chrono::floor<std::chrono::hours>(now) + std::chrono::hours(1);
std::this_thread::sleep_until(next_hour);
do_work();
}
}
8. std::this_thread::yield()
作用:当前线程主动让出 CPU 时间片,提示调度器可以运行其他线程。
关键点:
- 不是睡眠,只是建议调度器重新调度
- 可能立即被重新调度(如果没有其他就绪线程)
- 适合忙等待场景
cpp
#include <thread>
#include <atomic>
#include <iostream>
std::atomic<bool> ready{false};
void worker() {
// 忙等待,但使用 yield 避免占满 CPU
while (!ready.load()) {
std::this_thread::yield(); // 让出 CPU,让其他线程运行
}
std::cout << "Worker proceeding" << std::endl;
}
int main() {
std::thread t(worker);
// 模拟准备工作
std::this_thread::sleep_for(std::chrono::seconds(1));
ready = true;
t.join();
}
yield() vs sleep_for(0ms) vs 忙等待:
| 方法 | CPU 使用 | 响应时间 | 适用场景 |
|---|---|---|---|
忙等待 while(!flag){} |
100% | 最快 | 极短等待(微秒级) |
yield() |
较低 | 较好 | 短等待,多线程协作 |
sleep_for(1ms) |
很低 | 一般 | 较长等待,省电优先 |
9. std::hardware_concurrency()
作用:返回系统支持的硬件并发线程数(通常是 CPU 核心数)。
cpp
#include <thread>
#include <iostream>
int main() {
unsigned int cores = std::thread::hardware_concurrency();
std::cout << "Number of cores: " << cores << std::endl;
std::cout << "Concurrent threads supported: " << cores << std::endl;
if (cores == 0) {
std::cout << "Information not available" << std::endl;
}
// 实际应用:创建最优数量的工作线程
unsigned int thread_count = std::max(1u, cores);
std::vector<std::thread> workers;
for (unsigned int i = 0; i < thread_count; ++i) {
workers.emplace_back([i]{
std::cout << "Worker " << i << " running" << std::endl;
});
}
for (auto& t : workers) {
t.join();
}
}
注意事项:
- 可能返回 0(表示信息不可用)
- 仅作为建议值,不是硬性限制
- 超线程核心可能被计入
- 实际最优线程数可能因任务类型而异
最佳实践:
cpp
unsigned int optimal_threads = std::thread::hardware_concurrency();
if (optimal_threads == 0) {
optimal_threads = 4; // 合理的默认值
}
// CPU 密集型任务:使用核心数
// IO 密集型任务:可以更多(例如 2x 核心数)
unsigned int workers_for_cpu_bound = optimal_threads;
unsigned int workers_for_io_bound = optimal_threads * 2;
完整示例:线程池管理器
cpp
#include <thread>
#include <vector>
#include <queue>
#include <functional>
#include <iostream>
#include <chrono>
class SimpleThreadPool {
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex mtx;
std::condition_variable cv;
bool stop = false;
public:
SimpleThreadPool(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(mtx);
cv.wait(lock, [this] {
return stop || !tasks.empty();
});
if (stop && tasks.empty()) return;
task = std::move(tasks.front());
tasks.pop();
}
task();
}
});
}
}
void enqueue(std::function<void()> task) {
{
std::lock_guard<std::mutex> lock(mtx);
tasks.push(task);
}
cv.notify_one();
}
~SimpleThreadPool() {
{
std::lock_guard<std::mutex> lock(mtx);
stop = true;
}
cv.notify_all();
for (std::thread& worker : workers) {
if (worker.joinable()) worker.join();
}
}
};
int main() {
auto cores = std::thread::hardware_concurrency();
std::cout << "Using " << cores << " threads" << std::endl;
SimpleThreadPool pool(cores);
for (int i = 0; i < 10; ++i) {
pool.enqueue([i] {
std::cout << "Task " << i << " on thread "
<< std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
});
}
std::this_thread::sleep_for(std::chrono::seconds(2));
return 0;
}
互斥量 (<mutex>) 详解
一、互斥量类型 (Mutex Types)
- std::mutex:基础独占锁,不支持递归和超时,性能最好。
- std::recursive_mutex:允许同一线程多次加锁,需对应次数解锁,用于递归函数。
- std::timed_mutex:支持 try_lock_for/until 超时尝试,避免无限等待。
- std::shared_mutex std::unique_lock (C++17):读写锁,多线程可同时读(共享锁),写独占,读多写少场景适用。
1. std::mutex
最基本的互斥量,提供独占式锁定。
核心特性:
- 不支持递归锁定(同一线程重复 lock 会导致死锁)
- 不支持超时
- 最轻量、性能最好
cpp
#include <mutex>
#include <thread>
#include <iostream>
std::mutex mtx;
int shared_counter = 0;
void increment() {
for (int i = 0; i < 100000; ++i) {
mtx.lock();
++shared_counter; // 临界区
mtx.unlock();
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Counter: " << shared_counter << std::endl; // 200000
}
⚠️ 错误示例 - 递归锁定导致死锁:
cpp
std::mutex mtx;
void recursive_func(int depth) {
mtx.lock(); // 第一次锁定成功
if (depth > 0) {
recursive_func(depth - 1); // 再次 lock(),同一线程,死锁!
}
mtx.unlock();
}
// 解决方案:使用 std::recursive_mutex
2. std::recursive_mutex
允许同一线程多次锁定,必须对应相同次数的 unlock()。
使用场景: 递归函数、嵌套调用需要同一锁的场景
cpp
#include <mutex>
#include <iostream>
std::recursive_mutex rec_mtx;
void recursive_func(int depth) {
rec_mtx.lock();
std::cout << "Depth: " << depth << ", Lock count: "
<< rec_mtx.try_lock() << std::endl; // try_lock 返回 false 表示已锁
rec_mtx.unlock(); // 注意:这里 unlock 只是减少计数
if (depth > 0) {
recursive_func(depth - 1);
}
}
int main() {
recursive_func(3);
// 必须解锁相同次数,否则其他线程无法获取
std::thread t([&]{
if (rec_mtx.try_lock()) {
std::cout << "Thread got lock" << std::endl;
rec_mtx.unlock();
} else {
std::cout << "Thread cannot get lock (still locked by main)" << std::endl;
}
});
t.join();
}
性能对比:
cpp
// 递归互斥量有额外开销
std::recursive_mutex rec_mtx; // 稍慢
std::mutex mtx; // 更快
// 设计建议:优先考虑重构代码避免递归锁
3. std::timed_mutex
支持超时机制的互斥量。
额外方法:
try_lock_for(duration)- 等待一段时间try_lock_until(time_point)- 等待到指定时间点
cpp
#include <mutex>
#include <chrono>
#include <thread>
#include <iostream>
std::timed_mutex tmtx;
void worker(int id) {
using namespace std::chrono_literals;
// 尝试获取锁,最多等待 100ms
if (tmtx.try_lock_for(100ms)) {
std::cout << "Thread " << id << " acquired lock" << std::endl;
std::this_thread::sleep_for(200ms); // 持有锁 200ms
tmtx.unlock();
} else {
std::cout << "Thread " << id << " timeout, cannot acquire lock" << std::endl;
}
}
int main() {
std::thread t1(worker, 1);
std::thread t2(worker, 2);
t1.join();
t2.join();
}
输出可能:
Thread 1 acquired lock
Thread 2 timeout, cannot acquire lock
实用场景 - 避免死锁:
cpp
std::timed_mutex mtx1, mtx2;
void safe_lock_both() {
using namespace std::chrono_literals;
while (true) {
if (mtx1.try_lock_for(10ms)) {
if (mtx2.try_lock_for(10ms)) {
// 成功获取两个锁
// ... 工作 ...
mtx2.unlock();
mtx1.unlock();
break;
} else {
mtx1.unlock(); // 释放已获取的锁,避免死锁
}
}
}
}
4. std::recursive_timed_mutex
结合递归和超时特性。
cpp
std::recursive_timed_mutex rec_tmtx;
void recursive_work(int depth) {
if (rec_tmtx.try_lock_for(std::chrono::milliseconds(50))) {
std::cout << "Locked at depth " << depth << std::endl;
if (depth > 0) {
recursive_work(depth - 1);
}
rec_tmtx.unlock();
} else {
std::cout << "Timeout at depth " << depth << std::endl;
}
}
5. std::shared_mutex (C++17)
读写锁,支持共享锁(读)和独占锁(写)。
特性:
- 多个线程可以同时持有共享锁(读)
- 独占锁(写)时不允许任何其他锁
- 适合读多写少的场景
cpp
#include <shared_mutex>
#include <mutex>
#include <vector>
#include <thread>
class ThreadSafeVector {
std::vector<int> data;
mutable std::shared_mutex mtx; // mutable 允许 const 方法中加锁
public:
void add(int value) {
std::unique_lock<std::shared_mutex> lock(mtx); // 独占锁
data.push_back(value);
}
int get(int index) const {
std::shared_lock<std::shared_mutex> lock(mtx); // 共享锁
return data[index];
}
size_t size() const {
std::shared_lock<std::shared_mutex> lock(mtx);
return data.size();
}
};
int main() {
ThreadSafeVector vec;
// 多个读线程可以并发
std::vector<std::thread> readers;
for (int i = 0; i < 10; ++i) {
readers.emplace_back([&vec] {
for (int j = 0; j < 1000; ++j) {
if (vec.size() > 0) {
volatile int v = vec.get(0); // 读操作
}
}
});
}
// 写线程需要独占
std::thread writer([&vec] {
for (int i = 0; i < 100; ++i) {
vec.add(i);
}
});
for (auto& t : readers) t.join();
writer.join();
}
二、互斥量操作方法
lock() / unlock() / try_lock()
cpp
std::mutex mtx;
// lock() - 阻塞直到获取锁
mtx.lock();
// ... 临界区 ...
mtx.unlock();
// try_lock() - 非阻塞尝试
if (mtx.try_lock()) {
// 成功获取锁
// ... 临界区 ...
mtx.unlock();
} else {
// 锁已被占用,做其他事
std::cout << "Busy, do something else" << std::endl;
}
重要规则:
lock()和unlock()必须成对出现- 不要忘记
unlock()(使用 RAII 避免) - 已持有锁的线程再次
lock()会导致死锁(除非是递归锁) unlock()前必须已持有锁,否则未定义行为
三、RAII 锁管理类
1. std::lock_guard
最简单的 RAII 包装器,构造时加锁,析构时解锁。
cpp
#include <mutex>
std::mutex mtx;
void safe_function() {
std::lock_guard<std::mutex> lock(mtx); // 构造时 lock()
// ... 临界区,即使抛出异常也会自动 unlock()
} // 析构时 unlock()
// 不能手动解锁,不能转移所有权
特点:
- 轻量,零开销(相比手动 lock/unlock)
- 不能提前解锁
- 不能移动
2. std::unique_lock
更灵活的 RAII 锁管理器。
灵活性:
- 可以延迟加锁
- 可以提前解锁
- 可以尝试加锁
- 支持超时
- 可移动
cpp
#include <mutex>
#include <chrono>
std::timed_mutex tmtx;
void flexible_lock() {
using namespace std::chrono_literals;
// 1. 延迟加锁
std::unique_lock<std::timed_mutex> lock(tmtx, std::defer_lock);
// 2. 尝试加锁
if (lock.try_lock()) {
std::cout << "Got lock immediately" << std::endl;
}
// 3. 带超时的加锁
if (lock.try_lock_for(100ms)) {
std::cout << "Got lock within 100ms" << std::endl;
}
// 4. 手动解锁
lock.unlock();
// ... 不需要锁的代码 ...
// 5. 再次加锁
lock.lock();
// 6. 转移所有权
std::unique_lock<std::timed_mutex> lock2 = std::move(lock); // lock 现在为空
} // lock2 析构时解锁
std::unique_lock 构造函数选项:
| 构造函数 | 行为 |
|---|---|
unique_lock(mtx) |
立即加锁(同 lock_guard) |
unique_lock(mtx, defer_lock) |
不加锁,稍后手动 lock() |
unique_lock(mtx, try_to_lock) |
尝试加锁,调用 try_lock() |
unique_lock(mtx, adopt_lock) |
假设已持有锁(不重复加锁) |
unique_lock(mtx, duration) |
timed_mutex 专用,try_lock_for |
unique_lock(mtx, time_point) |
timed_mutex 专用,try_lock_until |
实际应用 - 条件变量:
cpp
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
// unique_lock 是条件变量唯一支持的锁类型
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // wait 会临时解锁
性能对比:
lock_guard- 零开销,应优先使用unique_lock- 略有开销,仅在需要灵活性时使用
3. std::shared_lock (C++14)
配合 shared_mutex 使用的共享锁 RAII 包装器。
cpp
#include <shared_mutex>
#include <map>
class ThreadSafeCache {
std::map<std::string, std::string> cache;
mutable std::shared_mutex mtx;
public:
// 读操作 - 共享锁
std::string get(const std::string& key) const {
std::shared_lock lock(mtx); // C++17 可省略模板参数
auto it = cache.find(key);
return it != cache.end() ? it->second : "";
}
// 写操作 - 独占锁
void set(const std::string& key, const std::string& value) {
std::unique_lock lock(mtx);
cache[key] = value;
}
// 升级锁(从共享到独占)
bool update_if_exists(const std::string& key, const std::string& new_value) {
std::shared_lock read_lock(mtx);
auto it = cache.find(key);
if (it == cache.end()) {
return false;
}
// 升级:释放共享锁,获取独占锁
read_lock.unlock();
std::unique_lock write_lock(mtx);
// 需要重新验证(可能被其他线程修改)
it = cache.find(key);
if (it != cache.end()) {
it->second = new_value;
return true;
}
return false;
}
};
4. std::scoped_lock (C++17)
同时锁定多个互斥量,避免死锁。
cpp
#include <mutex>
std::mutex mtx1, mtx2, mtx3;
void transfer(int& from, int& to, int amount) {
// 同时锁定所有互斥量(使用避免死锁算法)
std::scoped_lock lock(mtx1, mtx2, mtx3);
from -= amount;
to += amount;
} // 所有锁自动释放
// 对比传统做法:
void transfer_old(int& from, int& to, int amount) {
std::lock(mtx1, mtx2, mtx3); // 同时锁定
std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
std::lock_guard<std::mutex> lock3(mtx3, std::adopt_lock);
from -= amount;
to += amount;
}
scoped_lock 的优势:
- 自动使用死锁避免算法
- 支持任意数量的互斥量
- 可推导模板参数(C++17)
cpp
// C++17 可自动推导类型
std::scoped_lock lock(mtx1, mtx2); // 不需要 <std::mutex, std::mutex>
四、全局辅助函数
std::lock(m1, m2, ...)
同时锁定多个互斥量,避免死锁。
算法: 使用"全部或无"策略,如果某个锁失败,释放已获取的锁并重试。
cpp
std::mutex a, b;
void deadlock_risk() {
// 危险:可能导致死锁
a.lock();
b.lock(); // 如果另一线程先锁 b 再锁 a,死锁
// ...
b.unlock();
a.unlock();
}
void safe_lock() {
// 安全:同时锁定,避免死锁
std::lock(a, b);
// 必须配合 adopt_lock 使用 RAII
std::lock_guard<std::mutex> lock_a(a, std::adopt_lock);
std::lock_guard<std::mutex> lock_b(b, std::adopt_lock);
// ... 临界区 ...
} // RAII 自动解锁
与 scoped_lock 的关系:
std::scoped_lock内部调用std::lockscoped_lock更简洁,优先使用
std::try_lock(m1, m2, ...)
尝试锁定多个互斥量,从第一个开始依次 try_lock()。
cpp
std::mutex m1, m2, m3;
int result = std::try_lock(m1, m2, m3);
if (result == -1) {
// 所有锁都获取成功
std::lock_guard<std::mutex> lock1(m1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(m2, std::adopt_lock);
std::lock_guard<std::mutex> lock3(m3, std::adopt_lock);
// ... 工作 ...
} else {
// result 是失败锁的索引(0-based)
std::cout << "Failed to lock mutex " << result << std::endl;
// 成功锁定的需要手动释放(因为没有 RAII)
}
std::call_once(flag, func, args...)
确保函数只执行一次,即使在多线程环境中。
cpp
#include <mutex>
std::once_flag init_flag;
class Singleton {
static Singleton* instance;
static void init() {
instance = new Singleton();
std::cout << "Singleton initialized" << std::endl;
}
public:
static Singleton& get() {
std::call_once(init_flag, init);
return *instance;
}
};
// 线程安全且高效的初始化
void thread_func() {
auto& s = Singleton::get(); // 只会初始化一次
}
典型应用:
- 单例模式
- 延迟初始化
- 资源只加载一次
五、最佳实践总结
选择指南
| 场景 | 推荐方案 |
|---|---|
| 简单临界区,不需要灵活性 | std::lock_guard |
| 需要提前解锁、尝试锁、超时 | std::unique_lock |
| 条件变量等待 | std::unique_lock |
| 读多写少 | std::shared_mutex + shared_lock |
| 递归函数 | std::recursive_mutex |
| 多个互斥量 | std::scoped_lock (C++17) |
| 避免死锁 | std::lock + adopt_lock |
| 单例/懒初始化 | std::call_once |
错误示例对比
cpp
// ❌ 错误:忘记解锁
void bad() {
mtx.lock();
if (error) return; // 忘记解锁!
mtx.unlock();
}
// ✅ 正确:使用 RAII
void good() {
std::lock_guard lock(mtx);
if (error) return; // 自动解锁
}
// ❌ 错误:死锁风险
void deadlock() {
std::lock_guard lock1(mtx1);
std::lock_guard lock2(mtx2); // 另一线程可能先锁 mtx2
}
// ✅ 正确:同时锁定
void safe() {
std::scoped_lock lock(mtx1, mtx2);
}
// ❌ 错误:冗余锁定
void redundant() {
std::lock_guard lock(mtx);
mtx.lock(); // 死锁!
}
使用建议
- 优先使用 RAII 锁管理 (
lock_guard、unique_lock),避免手动lock/unlock - 避免死锁 :使用
std::lock或std::scoped_lock同时获取多个锁 - 条件变量必须与互斥量配合 ,等待条件时使用
unique_lock - 简单场景用
std::async,精细控制用thread+future/promise - 共享数据用原子操作,复杂同步用互斥量+条件变量
条件变量 (<condition_variable>) 详解
一、条件变量类型
1. std::condition_variable
核心特性:
- 只能与
std::unique_lock<std::mutex>配合使用 - 等待、通知机制用于线程间同步
- 通常用于生产者-消费者模式
工作原理:
条件变量允许一个线程等待某个条件成立,另一个线程在条件满足时通知等待的线程。条件变量本身不维护条件状态,需要程序员手动检查条件。
cpp
#include <condition_variable>
#include <mutex>
#include <thread>
#include <iostream>
#include <queue>
std::queue<int> queue;
std::mutex mtx;
std::condition_variable cv;
bool done = false;
void producer() {
for (int i = 0; i < 10; ++i) {
{
std::lock_guard<std::mutex> lock(mtx);
queue.push(i);
std::cout << "Produced: " << i << std::endl;
}
cv.notify_one(); // 通知一个等待的消费者
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
{
std::lock_guard<std::mutex> lock(mtx);
done = true;
}
cv.notify_all(); // 通知所有等待的线程结束
}
void consumer(int id) {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
// 等待条件:队列非空 或 任务完成
cv.wait(lock, [&] {
return !queue.empty() || done;
});
if (!queue.empty()) {
int value = queue.front();
queue.pop();
lock.unlock(); // 提前解锁,允许其他消费者并行
std::cout << "Consumer " << id << " consumed: " << value << std::endl;
} else if (done) {
break;
}
}
std::cout << "Consumer " << id << " exiting" << std::endl;
}
int main() {
std::thread prod(producer);
std::thread cons1(consumer, 1);
std::thread cons2(consumer, 2);
prod.join();
cons1.join();
cons2.join();
}
2. std::condition_variable_any
核心特性:
- 可以与任何满足互斥量基本概念的类型配合使用
- 更通用但性能稍差(通常使用
std::condition_variable即可)
cpp
#include <condition_variable>
#include <shared_mutex>
std::shared_mutex sh_mtx;
std::condition_variable_any cv_any;
bool ready = false;
void reader() {
std::shared_lock lock(sh_mtx); // 共享锁
cv_any.wait(lock, [&] { return ready; }); // condition_variable_any 支持任何锁类型
// ... 读取操作 ...
}
void writer() {
std::unique_lock lock(sh_mtx); // 独占锁
ready = true;
cv_any.notify_all(); // 通知所有等待者
}
对比:
| 特性 | condition_variable |
condition_variable_any |
|---|---|---|
| 互斥量类型 | 仅 std::unique_lock<std::mutex> |
任何满足 BasicLockable 的类型 |
| 性能 | 更优 | 稍差(虚函数调用) |
| 使用场景 | 99% 的情况 | 特殊需求(如使用 shared_lock) |
二、等待函数 (Wait Functions)
1. wait(lock, predicate) 和 wait(lock)
作用: 阻塞当前线程,直到收到通知且条件满足。
wait(lock) - 无谓词版本:
cpp
void wait(std::unique_lock<std::mutex>& lock);
wait(lock, predicate) - 带谓词版本(推荐):
cpp
template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);
关键行为:
- 原子地解锁互斥量并阻塞线程
- 被通知后,重新获取互斥量
- 检查谓词条件(如果提供)
- 如果条件不满足,继续等待
cpp
std::mutex mtx;
std::condition_variable cv;
bool data_ready = false;
int data = 0;
// ❌ 错误用法:可能虚假唤醒(spurious wakeup)
void bad_wait() {
std::unique_lock<std::mutex> lock(mtx);
if (!data_ready) { // if 而不是 while
cv.wait(lock); // 可能虚假唤醒后继续执行
}
// 使用 data(可能 data_ready 仍为 false!)
}
// ✅ 正确用法:使用 while 循环检查条件
void good_wait_while() {
std::unique_lock<std::mutex> lock(mtx);
while (!data_ready) { // 每次唤醒都重新检查
cv.wait(lock);
}
// 使用 data(保证条件为真)
}
// ✅ 更佳用法:使用带谓词的 wait(内部就是 while 循环)
void best_wait() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return data_ready; }); // 等价于上面的 while 循环
// 使用 data
}
虚假唤醒 (Spurious Wakeup):
- 即使没有调用
notify,线程也可能被唤醒 - 这是 POSIX 和 Windows API 允许的行为
- 必须使用谓词或 while 循环处理
2. wait_for(lock, duration, predicate)
作用: 等待指定的时长,超时后即使条件不满足也会返回。
cpp
std::condition_variable cv;
std::mutex mtx;
bool event_occurred = false;
void timed_wait_example() {
std::unique_lock<std::mutex> lock(mtx);
using namespace std::chrono_literals;
// 版本1:无谓词,返回状态
auto status = cv.wait_for(lock, 1s);
if (status == std::cv_status::no_timeout) {
std::cout << "Notified before timeout" << std::endl;
} else {
std::cout << "Timeout occurred" << std::endl;
}
// 版本2:带谓词,返回布尔值
bool condition_met = cv.wait_for(lock, 1s, []{ return event_occurred; });
if (condition_met) {
std::cout << "Condition became true" << std::endl;
} else {
std::cout << "Timeout, condition still false" << std::endl;
}
}
实际应用 - 带超时的等待:
cpp
class TimeoutQueue {
std::queue<int> queue;
std::mutex mtx;
std::condition_variable cv;
public:
bool push(int value, std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lock(mtx);
// 等待队列有空位(假设队列最大容量为 10)
bool success = cv.wait_for(lock, timeout, [this] {
return queue.size() < 10;
});
if (success) {
queue.push(value);
cv.notify_one(); // 通知消费者
return true;
}
return false; // 超时失败
}
bool pop(int& value, std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lock(mtx);
bool success = cv.wait_for(lock, timeout, [this] {
return !queue.empty();
});
if (success) {
value = queue.front();
queue.pop();
cv.notify_one(); // 通知生产者
return true;
}
return false;
}
};
返回值:
wait_for返回std::cv_status枚举:std::cv_status::no_timeout- 被通知唤醒std::cv_status::timeout- 超时唤醒
- 带谓词版本返回
bool:true- 条件满足false- 超时且条件不满足
3. wait_until(lock, time_point, predicate)
作用: 等待到指定的绝对时间点。
cpp
void wait_until_example() {
std::unique_lock<std::mutex> lock(mtx);
using namespace std::chrono;
// 等待到 2025-01-01 00:00:00
auto target_time = system_clock::from_time_t(time_t(1735689600));
// 版本1:无谓词
auto status = cv.wait_until(lock, target_time);
if (status == std::cv_status::no_timeout) {
std::cout << "Notified before deadline" << std::endl;
}
// 版本2:带谓词
bool done = cv.wait_until(lock, target_time, []{ return work_completed; });
if (done) {
std::cout << "Work completed on time" << std::endl;
} else {
std::cout << "Deadline reached, work incomplete" << std::endl;
}
}
实际应用 - 定时任务:
cpp
class TimedWorker {
std::mutex mtx;
std::condition_variable cv;
bool stop = false;
public:
void run_until(std::chrono::system_clock::time_point deadline) {
std::unique_lock<std::mutex> lock(mtx);
while (!stop) {
if (cv.wait_until(lock, deadline, [this] { return stop; })) {
break; // 被停止信号唤醒
}
// 超时,执行定时任务
std::cout << "Deadline reached, executing task" << std::endl;
break;
}
}
void stop_worker() {
{
std::lock_guard<std::mutex> lock(mtx);
stop = true;
}
cv.notify_all();
}
};
三、通知函数 (Notify Functions)
1. notify_one()
作用: 唤醒一个等待该条件变量的线程。
cpp
void notify_one_example() {
std::unique_lock<std::mutex> lock(mtx);
// ... 修改共享数据 ...
cv.notify_one(); // 唤醒一个等待线程
}
重要特性:
- 如果没有等待线程,调用无效(不记录通知)
- 不会释放锁,锁会在
notify_one调用后保持 - 被唤醒的线程需要重新获取锁
cpp
std::mutex mtx;
std::condition_variable cv;
std::queue<int> tasks;
void producer() {
for (int i = 0; i < 100; ++i) {
{
std::lock_guard<std::mutex> lock(mtx);
tasks.push(i);
}
cv.notify_one(); // 只唤醒一个消费者
// 注意:锁已经释放,消费者可以获取锁
}
}
void consumer(int id) {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !tasks.empty(); });
int task = tasks.front();
tasks.pop();
lock.unlock(); // 提前解锁,允许其他消费者
std::cout << "Consumer " << id << " processing task " << task << std::endl;
if (task == 99) break;
}
}
2. notify_all()
作用: 唤醒所有等待该条件变量的线程。
cpp
void broadcast_example() {
std::unique_lock<std::mutex> lock(mtx);
// ... 重要状态改变 ...
cv.notify_all(); // 唤醒所有等待线程
}
典型应用场景:
cpp
class Barrier {
std::mutex mtx;
std::condition_variable cv;
int count;
const int total;
public:
Barrier(int n) : count(0), total(n) {}
void wait() {
std::unique_lock<std::mutex> lock(mtx);
if (++count == total) {
// 最后一个线程到达,唤醒所有人
cv.notify_all();
} else {
cv.wait(lock, [this] { return count == total; });
}
}
};
void worker(Barrier& barrier, int id) {
std::cout << "Worker " << id << " starting phase 1" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100 * id));
std::cout << "Worker " << id << " waiting at barrier" << std::endl;
barrier.wait(); // 等待所有线程
std::cout << "Worker " << id << " proceeding to phase 2" << std::endl;
}
notify_one vs notify_all:
| 场景 | 推荐 |
|---|---|
| 生产者-消费者(单消费者) | notify_one |
| 生产者-消费者(多消费者) | notify_one(各取一个任务) |
| 多个线程等待同一事件(如屏障) | notify_all |
| 状态改变影响所有等待线程 | notify_all |
性能注意:
notify_one比notify_all开销小- 不必要的
notify_all会导致"惊群效应"(thundering herd)
四、完整应用示例
示例 1:有界阻塞队列(生产者-消费者)
cpp
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
#include <iostream>
#include <chrono>
template<typename T>
class BoundedBlockingQueue {
std::queue<T> queue;
std::mutex mtx;
std::condition_variable not_full;
std::condition_variable not_empty;
size_t capacity;
bool shutdown = false;
public:
BoundedBlockingQueue(size_t cap) : capacity(cap) {}
void push(T value) {
std::unique_lock<std::mutex> lock(mtx);
// 等待队列有空间
not_full.wait(lock, [this] {
return queue.size() < capacity || shutdown;
});
if (shutdown) return;
queue.push(std::move(value));
not_empty.notify_one(); // 通知消费者
}
bool pop(T& value) {
std::unique_lock<std::mutex> lock(mtx);
// 等待队列非空或关闭
not_empty.wait(lock, [this] {
return !queue.empty() || shutdown;
});
if (shutdown && queue.empty()) return false;
value = std::move(queue.front());
queue.pop();
not_full.notify_one(); // 通知生产者
return true;
}
void stop() {
{
std::lock_guard<std::mutex> lock(mtx);
shutdown = true;
}
not_full.notify_all();
not_empty.notify_all();
}
};
int main() {
BoundedBlockingQueue<int> queue(5);
// 生产者
std::thread producer([&] {
for (int i = 0; i < 20; ++i) {
queue.push(i);
std::cout << "Produced: " << i << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
queue.stop();
});
// 消费者
std::thread consumer([&] {
int value;
while (queue.pop(value)) {
std::cout << "Consumed: " << value << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cout << "Consumer exiting" << std::endl;
});
producer.join();
consumer.join();
return 0;
}
示例 2:读写锁(使用条件变量实现)
cpp
class ReadWriteLock {
std::mutex mtx;
std::condition_variable cv;
int readers = 0;
bool writer = false;
int pending_writers = 0;
public:
void read_lock() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this] {
return !writer && pending_writers == 0; // 优先读者策略
});
++readers;
}
void read_unlock() {
std::unique_lock<std::mutex> lock(mtx);
if (--readers == 0) {
cv.notify_all(); // 可能有等待的写者
}
}
void write_lock() {
std::unique_lock<std::mutex> lock(mtx);
++pending_writers;
cv.wait(lock, [this] {
return !writer && readers == 0;
});
--pending_writers;
writer = true;
}
void write_unlock() {
std::unique_lock<std::mutex> lock(mtx);
writer = false;
cv.notify_all(); // 唤醒所有等待的读者和写者
}
};
示例 3:多线程任务池
cpp
class ThreadPool {
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex mtx;
std::condition_variable cv;
bool stop = false;
public:
explicit 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(mtx);
cv.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::lock_guard<std::mutex> lock(mtx);
tasks.emplace(std::forward<F>(f));
}
cv.notify_one(); // 唤醒一个工作线程
}
~ThreadPool() {
{
std::lock_guard<std::mutex> lock(mtx);
stop = true;
}
cv.notify_all();
for (auto& worker : workers) {
if (worker.joinable()) worker.join();
}
}
};
五、常见陷阱与最佳实践
最佳实践
- 始终使用谓词版本的
wait
cpp
// ✅ 正确
cv.wait(lock, []{ return condition; });
// ❌ 错误(可能虚假唤醒)
cv.wait(lock);
- 在修改共享状态后立即通知
cpp
// ✅ 正确
{
std::lock_guard lock(mtx);
data_ready = true;
cv.notify_one(); // 在锁内通知
}
- 使用
unique_lock而非lock_guard
cpp
// ✅ 正确
std::unique_lock<std::mutex> lock(mtx);
// ❌ 错误(wait 需要 unique_lock)
std::lock_guard<std::mutex> lock(mtx);
cv.wait(lock); // 编译错误!
- 优先使用
notify_one而非notify_all
cpp
// 除非所有线程都需要被唤醒,否则使用 notify_one
cv.notify_one(); // 更高效
常见错误
- 忘记条件检查
cpp
// ❌ 危险
cv.wait(lock); // 虚假唤醒后条件可能不满足
- 在锁外通知(可能丢失通知)
cpp
// ❌ 危险
data_ready = true;
lock.unlock();
cv.notify_one(); // 通知可能在 wait 之前到达
- 等待条件变量时持有其他锁
cpp
// ❌ 可能导致死锁
std::lock(mtx1, mtx2);
cv.wait(lock1); // 锁 mtx2 仍被持有
- 在析构函数中未通知
cpp
// ❌ 可能永远阻塞
~ThreadPool() {
// 忘记通知等待线程
}
异步任务 (<future>) 详解
C++ 异步任务 (<future>) 是什么?
核心概念 :<future> 是 C++11 引入的异步编程工具,让你发起一个可能耗时操作后,不阻塞等待结果,而在未来某个时刻获取它。
通俗理解
就像你在餐厅点餐:
- 同步方式:站在柜台前盯着厨师做完才离开(阻塞)
- 异步方式:点完拿号回座位,菜好了服务员通知你取餐(非阻塞)
future 就是那个"小票"------代表一个尚未就绪但将来会有的结果。
三大核心组件
| 组件 | 角色 | 比喻 |
|---|---|---|
std::async |
发起异步任务 | 向厨房下单 |
std::future |
结果凭证 | 取餐小票 |
std::promise |
手动设置结果 | 你承诺稍后给结果 |
基本用法
cpp
// 发起异步任务,自动得到 future
std::future<int> fut = std::async([]() {
std::this_thread::sleep_for(1s); // 模拟耗时
return 42;
});
// 主线程可以做其他事...
int result = fut.get(); // 需要结果时阻塞等待
为什么需要它?
- 避免阻塞主线程:UI 程序不卡顿、服务器继续处理其他请求
- 轻松并行 :
auto f1 = async(task1); auto f2 = async(task2);两个任务自动并发 - 异常传递 :子线程抛出的异常会在
get()时重新抛出 - 超时控制:可以设置最长等待时间
与其他工具的区别
| 工具 | 适用场景 |
|---|---|
std::thread |
需要精细控制线程生命周期(如 detach) |
std::async + future |
只需获取计算结果,不关心线程管理 |
std::packaged_task |
需要把异步任务包装成可调用对象传递 |
std::promise |
需要手动设置异步结果(如回调适配) |
一句话总结 :<future> 让你"发起任务 → 继续干活 → 需要时拿结果",是 C++ 中最简单的异步编程方式。
一、核心类
1. std::future<T>
作用: 表示一个异步操作的结果,可以从异步任务中获取返回值或异常。
核心特性:
- 只能移动,不能拷贝
- 只能获取一次结果(
get()后状态失效) - 支持等待、超时等操作
cpp
#include <future>
#include <iostream>
#include <thread>
#include <chrono>
int long_computation(int x) {
std::this_thread::sleep_for(std::chrono::seconds(1));
return x * x;
}
int main() {
// 创建异步任务
std::future<int> fut = std::async(std::launch::async, long_computation, 5);
std::cout << "Waiting for result..." << std::endl;
// 获取结果(阻塞直到就绪)
int result = fut.get();
std::cout << "Result: " << result << std::endl;
// fut.get(); // 错误!只能调用一次
return 0;
}
状态检查:
cpp
std::future<int> fut1; // 默认构造,无效
std::future<int> fut2 = std::async([](){ return 42; });
std::cout << "fut1 valid: " << fut1.valid() << std::endl; // false
std::cout << "fut2 valid: " << fut2.valid() << std::endl; // true
fut2.get();
std::cout << "After get, valid: " << fut2.valid() << std::endl; // false
2. std::shared_future<T>
作用: future 的共享版本,可以多次获取结果,可拷贝。
核心特性:
- 可以拷贝,多个对象共享同一个异步结果
- 可以多次调用
get() - 适合多个线程等待同一个结果
cpp
#include <future>
#include <thread>
#include <vector>
int expensive_calculation() {
std::this_thread::sleep_for(std::chrono::seconds(2));
return 100;
}
int main() {
// 方式1:从 future 转移
std::future<int> fut = std::async(expensive_calculation);
std::shared_future<int> shared_fut = std::move(fut);
// 方式2:直接创建(C++11 需要显式类型)
auto shared_fut2 = std::async(expensive_calculation).share();
// 多个线程可以同时等待同一个结果
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
threads.emplace_back([shared_fut] {
int result = shared_fut.get(); // 多次调用没问题
std::cout << "Thread got: " << result << std::endl;
});
}
for (auto& t : threads) {
t.join();
}
// 可以再次调用 get()
int result = shared_fut.get(); // 仍然有效
std::cout << "Main got: " << result << std::endl;
}
future vs shared_future:
| 特性 | future |
shared_future |
|---|---|---|
| 拷贝 | ❌ 只能移动 | ✅ 可拷贝 |
get() 次数 |
1 次 | 多次 |
| 内存开销 | 较小 | 略大 |
| 适用场景 | 单消费者 | 多消费者 |
3. std::promise<T>
作用: 存储一个值或异常,通过 future 异步获取。用于手动设置异步结果。
核心概念:
promise和future是配对使用的promise设置值,future获取值- 一对一的通道
cpp
#include <future>
#include <thread>
#include <iostream>
void process_data(std::promise<int> promise, int data) {
try {
// 模拟处理
if (data < 0) {
throw std::runtime_error("Negative data!");
}
int result = data * 2;
// 设置结果
promise.set_value(result);
} catch (...) {
// 设置异常
promise.set_exception(std::current_exception());
}
}
int main() {
std::promise<int> promise;
std::future<int> future = promise.get_future();
// 在另一个线程中处理
std::thread worker(process_data, std::move(promise), 42);
try {
int result = future.get(); // 等待结果
std::cout << "Result: " << result << std::endl;
} catch (const std::exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
worker.join();
}
多次设置的问题:
cpp
std::promise<int> promise;
promise.set_value(42);
// promise.set_value(100); // 错误!只能设置一次(抛出 future_error)
set_value_at_thread_exit:
cpp
void thread_func(std::promise<int> promise) {
// 计算...
promise.set_value_at_thread_exit(42); // 线程退出时自动设置
// 线程继续执行...
// 线程退出时值才变得可用
}
4. std::packaged_task<T>
作用: 包装可调用对象,使其可以异步执行,并自动提供 future。
核心特性:
- 包装函数、lambda、可调用对象
- 调用时自动设置
future的结果 - 可以被移动但不能拷贝
cpp
#include <future>
#include <iostream>
#include <thread>
int add(int a, int b) {
return a + b;
}
int main() {
// 包装函数
std::packaged_task<int(int, int)> task(add);
std::future<int> future = task.get_future();
// 在另一个线程中执行
std::thread t(std::move(task), 10, 20);
int result = future.get(); // 获取结果
std::cout << "10 + 20 = " << result << std::endl;
t.join();
}
包装 lambda:
cpp
std::packaged_task<int(int)> task([](int x) {
return x * x;
});
auto future = task.get_future();
std::thread t(std::move(task), 5);
std::cout << "Square: " << future.get() << std::endl;
t.join();
可重用(重置):
cpp
std::packaged_task<int()> task([](){ return 42; });
// 执行第一次
task();
auto result1 = task.get_future().get(); // 42
// 重置后重用
task.reset(); // 清除之前的结果
auto future = task.get_future();
task();
auto result2 = future.get(); // 42
二、创建异步任务的函数
std::async
作用: 异步执行一个函数,返回 future 对象。
启动策略:
cpp
std::launch::async // 立即在新线程中执行
std::launch::deferred // 延迟执行(调用 get/wait 时执行)
std::launch::async | std::launch::deferred // 实现选择(默认)
cpp
#include <future>
#include <iostream>
#include <chrono>
int heavy_work() {
std::this_thread::sleep_for(std::chrono::seconds(1));
return 42;
}
int main() {
using namespace std::chrono_literals;
// 策略1:async - 立即在新线程执行
auto fut1 = std::async(std::launch::async, heavy_work);
std::cout << "Async: " << fut1.get() << std::endl;
// 策略2:deferred - 延迟到 get() 时执行
auto fut2 = std::async(std::launch::deferred, heavy_work);
std::this_thread::sleep_for(2s);
std::cout << "Deferred: " << fut2.get() << std::endl; // 此时才执行
// 策略3:默认(实现选择)
auto fut3 = std::async(heavy_work); // 通常等同于 async|deferred
// 策略4:立即执行,但可以等待
auto fut4 = std::async(std::launch::async, heavy_work);
std::future_status status = fut4.wait_for(100ms);
if (status == std::future_status::timeout) {
std::cout << "Still running..." << std::endl;
} else if (status == std::future_status::ready) {
std::cout << "Result: " << fut4.get() << std::endl;
}
}
实际应用示例:
cpp
// 并发执行多个任务
auto future1 = std::async(std::launch::async, []{ return compute_1(); });
auto future2 = std::async(std::launch::async, []{ return compute_2(); });
auto future3 = std::async(std::launch::async, []{ return compute_3(); });
int result = future1.get() + future2.get() + future3.get();
// 条件执行
auto future = std::async(std::launch::deferred, []{
return expensive_operation();
});
if (need_result) {
int value = future.get(); // 只在需要时计算
}
三、等待函数
1. wait()
阻塞直到结果可用。
cpp
std::future<int> fut = std::async([]{
std::this_thread::sleep_for(std::chrono::seconds(1));
return 42;
});
std::cout << "Waiting..." << std::endl;
fut.wait(); // 阻塞直到任务完成
std::cout << "Ready!" << std::endl;
std::cout << fut.get() << std::endl;
2. wait_for(duration)
等待一段时间,返回状态。
cpp
std::future<int> fut = std::async(std::launch::async, []{
std::this_thread::sleep_for(std::chrono::seconds(2));
return 100;
});
using namespace std::chrono_literals;
auto status = fut.wait_for(1s);
if (status == std::future_status::timeout) {
std::cout << "Task still running after 1s" << std::endl;
} else if (status == std::future_status::ready) {
std::cout << "Task completed: " << fut.get() << std::endl;
} else if (status == std::future_status::deferred) {
std::cout << "Task is deferred" << std::endl;
}
std::future_status 枚举:
future_status::ready- 结果就绪future_status::timeout- 超时future_status::deferred- 任务延迟执行(未开始)
3. wait_until(time_point)
等待到指定时间点。
cpp
auto start = std::chrono::steady_clock::now();
auto deadline = start + std::chrono::seconds(2);
std::future<int> fut = std::async([]{
std::this_thread::sleep_for(std::chrono::seconds(1));
return 42;
});
auto status = fut.wait_until(deadline);
if (status == std::future_status::ready) {
std::cout << "Result: " << fut.get() << std::endl;
} else {
std::cout << "Timeout" << std::endl;
}
四、获取结果
get()
阻塞获取结果(只能调用一次)。
cpp
std::future<int> fut = std::async([](){ return 123; });
int value = fut.get(); // 阻塞直到就绪
异常处理:
cpp
auto fut = std::async([]{
throw std::runtime_error("Something went wrong");
return 0;
});
try {
int result = fut.get();
} catch (const std::exception& e) {
std::cout << "Caught: " << e.what() << std::endl;
}
五、完整应用示例
示例 1:并行计算求和
cpp
#include <future>
#include <vector>
#include <numeric>
#include <iostream>
int sum_range(const std::vector<int>& data, size_t start, size_t end) {
return std::accumulate(data.begin() + start, data.begin() + end, 0);
}
int parallel_sum(const std::vector<int>& data, size_t num_threads) {
if (data.empty()) return 0;
size_t chunk_size = (data.size() + num_threads - 1) / num_threads;
std::vector<std::future<int>> futures;
for (size_t i = 0; i < num_threads; ++i) {
size_t start = i * chunk_size;
size_t end = std::min(start + chunk_size, data.size());
if (start >= data.size()) break;
futures.push_back(std::async(std::launch::async, sum_range,
std::ref(data), start, end));
}
int total = 0;
for (auto& fut : futures) {
total += fut.get();
}
return total;
}
int main() {
std::vector<int> data(1000000, 1); // 一百万个 1
int sum = parallel_sum(data, 4);
std::cout << "Sum: " << sum << std::endl; // 1000000
}
示例 2:生产者-消费者使用 promise
cpp
#include <future>
#include <queue>
#include <thread>
#include <iostream>
class RequestQueue {
std::queue<std::promise<int>> promises;
std::queue<int> requests;
std::mutex mtx;
std::condition_variable cv;
bool stop = false;
public:
void stop_server() {
{
std::lock_guard<std::mutex> lock(mtx);
stop = true;
}
cv.notify_all();
}
std::future<int> submit_request(int data) {
std::promise<int> promise;
auto future = promise.get_future();
{
std::lock_guard<std::mutex> lock(mtx);
requests.push(data);
promises.push(std::move(promise));
}
cv.notify_one();
return future;
}
void process_requests() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this] {
return !requests.empty() || stop;
});
if (stop && requests.empty()) break;
int data = requests.front();
requests.pop();
auto promise = std::move(promises.front());
promises.pop();
lock.unlock();
try {
// 处理请求
int result = data * 2;
promise.set_value(result);
} catch (...) {
promise.set_exception(std::current_exception());
}
}
}
};
int main() {
RequestQueue queue;
std::thread worker(&RequestQueue::process_requests, &queue);
std::vector<std::future<int>> futures;
for (int i = 1; i <= 5; ++i) {
futures.push_back(queue.submit_request(i));
}
for (auto& fut : futures) {
std::cout << "Result: " << fut.get() << std::endl;
}
queue.stop_server();
worker.join();
}
示例 3:使用 packaged_task 实现线程池
cpp
#include <future>
#include <queue>
#include <thread>
#include <functional>
#include <vector>
#include <iostream>
class ThreadPool {
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex mtx;
std::condition_variable cv;
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(mtx);
cv.wait(lock, [this] {
return stop || !tasks.empty();
});
if (stop && tasks.empty()) return;
task = std::move(tasks.front());
tasks.pop();
}
task();
}
});
}
}
template<typename F, typename... Args>
auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared<std::packaged_task<return_type()>>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> result = task->get_future();
{
std::lock_guard<std::mutex> lock(mtx);
if (stop) {
throw std::runtime_error("enqueue on stopped ThreadPool");
}
tasks.emplace([task]() { (*task)(); });
}
cv.notify_one();
return result;
}
~ThreadPool() {
{
std::lock_guard<std::mutex> lock(mtx);
stop = true;
}
cv.notify_all();
for (auto& worker : workers) {
if (worker.joinable()) worker.join();
}
}
};
int main() {
ThreadPool pool(4);
std::vector<std::future<int>> futures;
for (int i = 0; i < 10; ++i) {
futures.push_back(pool.enqueue([i] {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return i * i;
}));
}
for (auto& fut : futures) {
std::cout << fut.get() << " ";
}
std::cout << std::endl;
}
示例 4:超时控制
cpp
#include <future>
#include <chrono>
#include <iostream>
std::string fetch_data_from_network() {
std::this_thread::sleep_for(std::chrono::seconds(3));
return "Data from server";
}
int main() {
using namespace std::chrono_literals;
auto future = std::async(std::launch::async, fetch_data_from_network);
// 等待最多 2 秒
if (future.wait_for(2s) == std::future_status::timeout) {
std::cout << "Timeout! Operation took too long" << std::endl;
// 注意:不能取消异步任务,只能忽略结果
// 任务会在后台继续执行直到完成
} else {
std::cout << "Result: " << future.get() << std::endl;
}
// 实际应用中可能需要处理超时后的清理
// 例如:设置一个标志让任务提前退出
}
六、常见陷阱与最佳实践
✅ 最佳实践
- 优先使用
std::async而非手动创建线程
cpp
// ✅ 更简单、安全
auto fut = std::async(func, args);
// ❌ 需要手动管理
std::thread t(func, args);
t.join();
- 使用
shared_future处理多消费者
cpp
// ✅ 多个线程可以获取结果
auto shared_fut = async(func).share();
- 合理使用启动策略
cpp
// 立即执行
std::async(std::launch::async, func);
// 延迟执行(节省资源)
std::async(std::launch::deferred, func);
- 异常安全
cpp
// ✅ 异常会自动传播
auto fut = std::async([]{
if (error) throw std::runtime_error("Error");
return result;
});
try {
result = fut.get();
} catch (...) {
// 处理异常
}
❌ 常见错误
- 忘记获取 future 结果
cpp
// ❌ 如果 async 使用默认策略,可能不会执行
std::async(func); // 临时对象析构,可能阻塞或忽略
// ✅ 保存 future 或指定 async 策略
auto fut = std::async(std::launch::async, func);
- 多次调用
get()
cpp
auto fut = std::async(func);
int a = fut.get();
int b = fut.get(); // 错误!
- 死锁
cpp
// ❌ 死锁:future 等待自己
auto fut = std::async([&fut] { return fut.get(); }, std::ref(fut));
- 忽略超时
cpp
// ❌ 可能永远阻塞
fut.wait();
// ✅ 使用超时
if (fut.wait_for(1s) == future_status::timeout) {
// 处理超时
}
需要我讲解原子操作 (<atomic>) 部分吗?
原子操作 (<atomic>) 详解
一、原子类型
1. std::atomic<T>
作用: 提供对基本类型的原子操作,保证多线程访问时的原子性。
支持的类型:
- 整型:
bool,char,int,long,long long等 - 指针类型
- 枚举类型
- 自定义类型(需满足
std::is_trivially_copyable_v<T>)
cpp
#include <atomic>
#include <thread>
#include <iostream>
#include <vector>
std::atomic<int> counter{0}; // 原子计数器
void increment(int times) {
for (int i = 0; i < times; ++i) {
counter++; // 原子自增
// 等价于 counter.fetch_add(1);
}
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back(increment, 10000);
}
for (auto& t : threads) {
t.join();
}
std::cout << "Counter: " << counter.load() << std::endl; // 100000
}
自定义类型:
cpp
struct Point {
int x, y;
// 必须满足 trivially copyable
};
std::atomic<Point> atom_point;
void update_point() {
Point p{10, 20};
atom_point.store(p); // 原子存储
Point q = atom_point.load(); // 原子读取
std::cout << "Point: " << q.x << "," << q.y << std::endl;
}
常用特化:
cpp
std::atomic_bool // std::atomic<bool>
std::atomic_char // std::atomic<char>
std::atomic_int // std::atomic<int>
std::atomic_long // std::atomic<long>
std::atomic_llong // std::atomic<long long>
std::atomic_uint // std::atomic<unsigned int>
std::atomic_size_t // std::atomic<size_t>
std::atomic_ptrdiff_t // std::atomic<ptrdiff_t>
2. std::atomic_flag
作用: 最小的原子布尔类型,保证无锁实现。用于实现自旋锁。
核心特性:
- 只有两个操作:
test_and_set()和clear() - 保证无锁(lock-free)
- 不能拷贝、移动
cpp
#include <atomic>
#include <thread>
#include <iostream>
std::atomic_flag lock = ATOMIC_FLAG_INIT; // 初始化为 false
void spin_lock() {
while (lock.test_and_set(std::memory_order_acquire)) {
// 自旋等待
}
}
void spin_unlock() {
lock.clear(std::memory_order_release);
}
int shared_data = 0;
void worker(int id) {
spin_lock();
shared_data++;
std::cout << "Thread " << id << " incremented to " << shared_data << std::endl;
spin_unlock();
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back(worker, i);
}
for (auto& t : threads) {
t.join();
}
}
实现自旋锁类:
cpp
class SpinLock {
std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock() {
while (flag.test_and_set(std::memory_order_acquire)) {
// 自旋等待
}
}
void unlock() {
flag.clear(std::memory_order_release);
}
bool try_lock() {
return !flag.test_and_set(std::memory_order_acquire);
}
};
// 使用示例
SpinLock slock;
std::lock_guard<SpinLock> lock(slock); // RAII 支持
3. std::atomic_ref<T> (C++20)
作用: 对非原子对象提供原子操作。
cpp
#if __cplusplus >= 202002L
#include <atomic>
int normal_int = 0;
std::atomic_ref<int> atomic_int(normal_int);
void increment() {
for (int i = 0; i < 10000; ++i) {
atomic_int++; // 原子操作 normal_int
}
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back(increment);
}
for (auto& t : threads) {
t.join();
}
std::cout << normal_int << std::endl; // 100000
}
#endif
二、原子操作函数
基本操作
cpp
std::atomic<int> a{10};
// 1. store - 存储值
a.store(20); // 设置值为 20
a.store(30, std::memory_order_release); // 带内存顺序
// 2. load - 读取值
int x = a.load(); // 读取当前值
int y = a.load(std::memory_order_acquire);
// 3. exchange - 交换并返回旧值
int old = a.exchange(40); // 返回 30,设置新值为 40
// 4. compare_exchange_weak/strong - CAS 操作
int expected = 40;
if (a.compare_exchange_strong(expected, 50)) {
std::cout << "CAS succeeded, new value: " << a.load() << std::endl;
} else {
std::cout << "CAS failed, expected was: " << expected << std::endl;
}
算术操作(整型特化)
cpp
std::atomic<int> counter{0};
// fetch_add - 原子加法,返回旧值
int old = counter.fetch_add(5); // old = 0, counter = 5
int old = counter.fetch_sub(2); // old = 5, counter = 3
// 运算符重载
counter++; // 等价于 fetch_add(1)
++counter; // 等价于 fetch_add(1) + 1
counter += 3; // 等价于 fetch_add(3) + 3
// 其他操作
counter.fetch_and(0xFF); // 位与
counter.fetch_or(0x80); // 位或
counter.fetch_xor(0xFF); // 异或
完整示例:
cpp
std::atomic<int> value{10};
std::cout << value.fetch_add(5) << std::endl; // 10 (value = 15)
std::cout << value.fetch_sub(3) << std::endl; // 15 (value = 12)
std::cout << value.fetch_and(0xF) << std::endl; // 12 (value = 12 & 0xF = 12)
std::cout << value.load() << std::endl; // 12
value = 20; // 隐式 store
int x = value; // 隐式 load
指针操作
cpp
struct Data { int a, b; };
std::atomic<Data*> ptr{nullptr};
Data data1, data2;
// fetch_add - 指针偏移
ptr.store(&data1);
Data* old = ptr.fetch_add(1); // 等价于 ptr += sizeof(Data),返回旧指针
Data* new_ptr = ptr.load(); // &data1 + 1
// 比较交换
Data* expected = &data1;
if (ptr.compare_exchange_strong(expected, &data2)) {
std::cout << "Pointer updated" << std::endl;
}
三、内存顺序
std::memory_order 枚举
| 内存顺序 | 说明 | 适用场景 |
|---|---|---|
memory_order_relaxed |
最弱顺序,仅保证原子性 | 计数器 |
memory_order_consume |
依赖顺序(几乎不用) | 特殊优化 |
memory_order_acquire |
后续读/写不能重排到此操作前 | 读锁 |
memory_order_release |
此前读/写不能重排到此操作后 | 写锁 |
memory_order_acq_rel |
acquire + release | RMW 操作 |
memory_order_seq_cst |
最强顺序,全局一致(默认) | 通用 |
cpp
#include <atomic>
#include <thread>
#include <iostream>
#include <vector>
std::atomic<bool> ready{false};
std::atomic<int> data{0};
// 使用 relaxed 的计数器
std::atomic<int> counter{0};
void relaxed_counter() {
for (int i = 0; i < 10000; ++i) {
counter.fetch_add(1, std::memory_order_relaxed);
}
}
// 使用 acquire-release 同步
void producer() {
data.store(42, std::memory_order_relaxed);
ready.store(true, std::memory_order_release); // 发布
}
void consumer() {
while (!ready.load(std::memory_order_acquire)); // 获取
// 保证看到 data 的写入
std::cout << "Data: " << data.load(std::memory_order_relaxed) << std::endl;
}
// 顺序一致性
std::atomic<bool> x{false}, y{false};
std::atomic<int> z{0};
void write_x() {
x.store(true, std::memory_order_seq_cst);
}
void write_y() {
y.store(true, std::memory_order_seq_cst);
}
void read_x_then_y() {
while (!x.load(std::memory_order_seq_cst));
if (y.load(std::memory_order_seq_cst)) {
z++;
}
}
void read_y_then_x() {
while (!y.load(std::memory_order_seq_cst));
if (x.load(std::memory_order_seq_cst)) {
z++;
}
}
内存顺序选择指南:
cpp
// 1. 默认使用 seq_cst(最简单)
counter.fetch_add(1); // 默认 seq_cst
// 2. 计数器可以用 relaxed
counter.fetch_add(1, std::memory_order_relaxed);
// 3. 生产者-消费者用 release-acquire
producer.store(true, std::memory_order_release);
consumer.load(std::memory_order_acquire);
// 4. 自旋锁用 acquire-release
while (flag.test_and_set(std::memory_order_acquire));
flag.clear(std::memory_order_release);
四、无锁编程函数
is_lock_free() / is_always_lock_free
检查原子操作是否无锁。
cpp
std::atomic<int> a;
std::atomic<long long> b;
std::cout << std::boolalpha;
std::cout << "int is lock-free: " << a.is_lock_free() << std::endl;
std::cout << "int always lock-free: " << std::atomic<int>::is_always_lock_free << std::endl;
// 运行时检查
if (a.is_lock_free()) {
std::cout << "Using lock-free implementation" << std::endl;
} else {
std::cout << "Using mutex-based implementation" << std::endl;
}
atomic_signal_fence / atomic_thread_fence
内存屏障。
cpp
#include <atomic>
#include <thread>
std::atomic<bool> flag{false};
int data = 0;
// 线程 A
void thread_a() {
data = 42;
std::atomic_thread_fence(std::memory_order_release);
flag.store(true, std::memory_order_relaxed);
}
// 线程 B
void thread_b() {
while (!flag.load(std::memory_order_relaxed));
std::atomic_thread_fence(std::memory_order_acquire);
// 保证看到 data = 42
std::cout << data << std::endl;
}
五、完整应用示例
示例 1:无锁栈
cpp
#include <atomic>
#include <iostream>
template<typename T>
class LockFreeStack {
struct Node {
T data;
Node* next;
Node(const T& d) : data(d), next(nullptr) {}
};
std::atomic<Node*> head{nullptr};
public:
void push(const T& value) {
Node* new_node = new Node(value);
new_node->next = head.load(std::memory_order_relaxed);
// CAS 循环
while (!head.compare_exchange_weak(new_node->next, new_node,
std::memory_order_release,
std::memory_order_relaxed)) {
// 失败时 new_node->next 已更新为最新的 head
}
}
bool pop(T& result) {
Node* old_head = head.load(std::memory_order_acquire);
while (old_head &&
!head.compare_exchange_weak(old_head, old_head->next,
std::memory_order_acquire,
std::memory_order_relaxed)) {
// 重试
}
if (old_head) {
result = old_head->data;
delete old_head;
return true;
}
return false;
}
};
int main() {
LockFreeStack<int> stack;
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back([&stack, i] {
stack.push(i);
});
}
for (auto& t : threads) {
t.join();
}
int value;
while (stack.pop(value)) {
std::cout << value << " ";
}
std::cout << std::endl;
}
示例 2:无锁队列
cpp
#include <atomic>
#include <memory>
template<typename T>
class LockFreeQueue {
struct Node {
std::shared_ptr<T> data;
std::atomic<Node*> next;
Node() : next(nullptr) {}
Node(const T& value) : data(std::make_shared<T>(value)), next(nullptr) {}
};
std::atomic<Node*> head;
std::atomic<Node*> tail;
public:
LockFreeQueue() {
Node* dummy = new Node();
head.store(dummy, std::memory_order_relaxed);
tail.store(dummy, std::memory_order_relaxed);
}
void enqueue(const T& value) {
Node* new_node = new Node(value);
while (true) {
Node* last = tail.load(std::memory_order_acquire);
Node* next = last->next.load(std::memory_order_acquire);
if (last == tail.load(std::memory_order_acquire)) {
if (next == nullptr) {
if (last->next.compare_exchange_weak(next, new_node,
std::memory_order_release,
std::memory_order_relaxed)) {
tail.compare_exchange_weak(last, new_node,
std::memory_order_release,
std::memory_order_relaxed);
break;
}
} else {
tail.compare_exchange_weak(last, next,
std::memory_order_release,
std::memory_order_relaxed);
}
}
}
}
bool dequeue(T& result) {
while (true) {
Node* first = head.load(std::memory_order_acquire);
Node* last = tail.load(std::memory_order_acquire);
Node* next = first->next.load(std::memory_order_acquire);
if (first == head.load(std::memory_order_acquire)) {
if (first == last) {
if (next == nullptr) {
return false; // 队列为空
}
tail.compare_exchange_weak(last, next,
std::memory_order_release,
std::memory_order_relaxed);
} else {
if (next->data) {
result = *(next->data);
if (head.compare_exchange_weak(first, next,
std::memory_order_release,
std::memory_order_relaxed)) {
delete first;
return true;
}
}
}
}
}
}
~LockFreeQueue() {
T dummy;
while (dequeue(dummy));
delete head.load();
}
};
示例 3:引用计数(智能指针)
cpp
#include <atomic>
template<typename T>
class AtomicSharedPtr {
struct ControlBlock {
std::atomic<int> ref_count;
std::atomic<int> weak_count;
T* ptr;
ControlBlock(T* p) : ref_count(1), weak_count(0), ptr(p) {}
};
ControlBlock* cb;
public:
AtomicSharedPtr() : cb(nullptr) {}
explicit AtomicSharedPtr(T* ptr) : cb(new ControlBlock(ptr)) {}
AtomicSharedPtr(const AtomicSharedPtr& other) : cb(other.cb) {
if (cb) {
cb->ref_count.fetch_add(1, std::memory_order_relaxed);
}
}
~AtomicSharedPtr() {
if (cb && cb->ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
delete cb->ptr;
delete cb;
}
}
T* operator->() { return cb ? cb->ptr : nullptr; }
T& operator*() { return *(cb->ptr); }
void reset(T* ptr = nullptr) {
AtomicSharedPtr(ptr).swap(*this);
}
void swap(AtomicSharedPtr& other) {
std::swap(cb, other.cb);
}
int use_count() const {
return cb ? cb->ref_count.load(std::memory_order_relaxed) : 0;
}
};
示例 4:无锁单例模式
cpp
#include <atomic>
#include <memory>
class Singleton {
static std::atomic<Singleton*> instance;
static std::atomic<bool> initialized;
Singleton() = default;
public:
static Singleton* get() {
Singleton* ptr = instance.load(std::memory_order_acquire);
if (!ptr) {
ptr = new Singleton();
Singleton* expected = nullptr;
if (instance.compare_exchange_strong(expected, ptr,
std::memory_order_release,
std::memory_order_relaxed)) {
initialized.store(true, std::memory_order_release);
return ptr;
} else {
delete ptr;
return instance.load(std::memory_order_acquire);
}
}
return ptr;
}
static bool is_initialized() {
return initialized.load(std::memory_order_acquire);
}
};
std::atomic<Singleton*> Singleton::instance{nullptr};
std::atomic<bool> Singleton::initialized{false};
示例 5:高性能计数器
cpp
#include <atomic>
#include <vector>
#include <thread>
#include <iostream>
class Counter {
// 使用伪共享避免
struct alignas(64) CacheLine { // 64 字节对齐
std::atomic<long long> value{0};
char padding[64 - sizeof(std::atomic<long long>)];
};
std::vector<CacheLine> counters;
int num_threads;
public:
Counter(int threads) : num_threads(threads), counters(threads) {}
void increment(int thread_id) {
counters[thread_id].value.fetch_add(1, std::memory_order_relaxed);
}
long long total() const {
long long sum = 0;
for (const auto& counter : counters) {
sum += counter.value.load(std::memory_order_relaxed);
}
return sum;
}
};
int main() {
const int num_threads = 4;
const int iterations = 10000000;
Counter counter(num_threads);
std::vector<std::thread> threads;
for (int i = 0; i < num_threads; ++i) {
threads.emplace_back([&counter, i, iterations] {
for (int j = 0; j < iterations; ++j) {
counter.increment(i);
}
});
}
for (auto& t : threads) {
t.join();
}
std::cout << "Total: " << counter.total() << std::endl; // 40000000
}
六、常见陷阱与最佳实践
最佳实践
- 使用默认的内存顺序除非性能关键
cpp
// ✅ 简单,正确
counter++;
// ⚠️ 仅在分析后使用 relaxed
counter.fetch_add(1, std::memory_order_relaxed);
- 避免伪共享
cpp
// ❌ 可能伪共享
struct Bad {
std::atomic<int> a;
std::atomic<int> b; // 与 a 在同一缓存行
};
// ✅ 对齐到缓存行
struct Good {
alignas(64) std::atomic<int> a;
alignas(64) std::atomic<int> b;
};
- 使用 compare_exchange_weak 循环
cpp
// ✅ strong 适用于大多数情况
expected = current.load();
while (!atomic.compare_exchange_weak(expected, new_value)) {
// 重试
}
// ⚠️ weak 允许虚假失败,但性能更好
- 释放内存时注意顺序
cpp
// ✅ 确保所有操作完成后再删除
ptr.store(nullptr, std::memory_order_release);
delete old_ptr; // 安全
常见错误
- 忘记原子性
cpp
// ❌ 非原子操作
int counter = 0;
counter++; // 多线程不安全
// ✅ 原子操作
std::atomic<int> counter{0};
counter++;
- 内存顺序使用错误
cpp
// ❌ 错误的顺序可能导致数据竞争
ready.store(true, std::memory_order_relaxed);
data = 42; // 可能被重排到 store 之前
// ✅ 正确的顺序
data = 42;
ready.store(true, std::memory_order_release);
- CAS 循环错误
cpp
// ❌ 没有更新 expected
int expected = 10;
while (!atomic.compare_exchange_weak(expected, 20)) {
// expected 不会自动更新
}
// ✅ expected 会被自动更新
int expected = 10;
while (!atomic.compare_exchange_weak(expected, 20)) {
// expected 现在是实际值
}
- ABA 问题
cpp
// 问题场景:值从 A -> B -> A,CAS 可能错误成功
// 解决方案:使用带 tag 的指针或版本号
struct TaggedPtr {
void* ptr;
uint64_t tag;
};
std::atomic<TaggedPtr> atomic_ptr;
总结
| 类型/函数 | 用途 | 注意事项 |
|---|---|---|
std::atomic<T> |
通用原子类型 | 自定义类型必须 trivially copyable |
std::atomic_flag |
自旋锁 | 唯一保证无锁 |
load/store |
读写 | 指定内存顺序 |
exchange |
交换 | RMW 操作 |
compare_exchange_* |
CAS | 注意 ABA 问题 |
fetch_* |
算术/位运算 | 返回旧值 |
memory_order_* |
内存顺序 | 默认 seq_cst 最安全 |
写在最后
多线程编程是一把双刃剑,它能显著提升程序性能,但也引入了复杂性。在决定使用多线程之前,一定要先分析程序的瓶颈,确保多线程确实能带来收益。同时,编写多线程代码时要格外小心,仔细考虑所有可能的并发场景。