
C++11并发支持库
- 一、thread
- (一)thread类
- `std::thread` 就是 **“把函数扔给后台跑”** 的利器; 若多个线程同时改同一份数据,记得用 `std::atomic` 防止 race-condition。
- (二)命名空间域std::this_thread
- 二、mutex/lock_guard/unique_lock
- (一)互斥量/RAII智能锁
- (二)常用接口
- (三)用法介绍
- 三、时间命名空间chrono的一些成员
- 四、atomic
- (一)std::atomic 核心接口(store/load/exchange/CAS)
- 1、接口介绍
- 2、实例
- store / load
- exchange —— 实现自旋锁
- compare_exchange_weak
- compare_exchange_strong
- compare_exchange_strong
- (二)memory_order
- 1、为什么需要 memory_order
- 2、六种meory_order选项
- `memory_order_acquire`和`memory_order_release`
- `memory_order_seq_cst`
- 实战建议
- 五、conditon_variable
- 六、异步操作常用接口
- (一)std::async
- (二)std::promise
- (三)future / shared_future
- (四)package_task
一、thread
(一)thread类
1、构造函数
std::thread
就是 “把函数扔给后台跑” 的利器; 若多个线程同时改同一份数据,记得用 std::atomic
防止 race-condition。

接口参考https://legacy.cplusplus.com/reference/thread/thread/
三种最常用构造方式
场景 | 示范代码 | 注意 |
---|
① 全局函数 | std::thread(increase_global, 1000) | 直接传参即可 |
② 带引用参数 | std::thread(increase_reference, std::ref(foo), 1000) | std::thread 的传参系统不认引用,必须用 std::ref 强行“传引用”。,否则拷贝 |
③ 类成员函数 | std::thread(&C::increase_member, std::ref(bar), 1000) | 第一个实参是对象引用 |
2、常用接口
接口 | 原型 | 作用 | 使用时机 | 注意事项 |
---|
get_id | std::thread::id get_id() const noexcept | 获取线程唯一 ID | 调试、日志、区分线程 | 空线程返回 std::thread::id() |
joinable | bool joinable() const noexcept | 判断线程是否可 join | 调用 join/detach 前检查 | 默认构造、已 join/detach 都返回 false |
join | void join() | 阻塞等待线程结束 | 必须等待子线程完成 | 只能调一次;线程空或已 detach 抛 std::system_error |
detach | void detach() | 让线程后台运行,与主线程分离 | 不关心线程何时结束 | 一旦 detach 后无法再 join ;线程对象置空 |
3、 完整可运行示例
#include <iostream>
#include <atomic>
#include <thread>
#include <vector>
std::atomic<int> global_counter{0};
void increase_global(int n) {for (int i = 0; i < n; ++i) ++global_counter;
}
void increase_reference(std::atomic<int>& var, int n) {for (int i = 0; i < n; ++i) ++var;
}
struct C : std::atomic<int> {C() : std::atomic<int>(0) {}void increase_member(int n) {for (int i = 0; i < n; ++i) fetch_add(1);}
};int main() {std::vector<std::thread> threads;for (int i = 0; i < 10; ++i)threads.emplace_back(increase_global, 1000);std::atomic<int> foo{0};for (int i = 0; i < 10; ++i)threads.emplace_back(increase_reference, std::ref(foo), 1000);C bar;for (int i = 0; i < 10; ++i)threads.emplace_back(&C::increase_member, std::ref(bar), 1000);for (auto& th : threads) th.join();std::cout << "global_counter = " << global_counter << '\n'<< "foo = " << foo << '\n'<< "bar = " << bar << '\n';return 0;
}
(二)命名空间域std::this_thread
1、四个成员函数
函数 | 原型 | 作用 | 示例代码 | 备注 |
---|
get_id | std::thread::id get_id() const noexcept | 获取当前线程的唯一 ID | std::cout << std::this_thread::get_id(); | 空线程返回默认构造的 id() |
yield | void yield() noexcept | 主动让出当前线程剩余时间片 | std::this_thread::yield(); | 提示调度器“我闲着”,不保证立刻切换 |
sleep_until | template< class Clock, class Duration > void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time ) | 阻塞到绝对时间点 | std::this_thread::sleep_until(tp); | 时间未到线程保持阻塞 |
sleep_for | template< class Rep, class Period > void sleep_for( const std::chrono::duration<Rep,Period>& rel_time ) | 阻塞相对时长 | std::this_thread::sleep_for(500ms); | 最常用,单位可自动推导 |
2、用法用例
#include <iostream>
#include <thread>
#include <chrono>int main()
{using namespace std::chrono_literals;std::cout << "main thread id = " << std::this_thread::get_id() << '\n';std::cout << "yield example (no output, just hint)\n";std::this_thread::yield();std::cout << "sleep 500ms...\n";std::this_thread::sleep_for(500ms);auto tp = std::chrono::steady_clock::now() + 1s;std::cout << "sleep until 1s later...\n";std::this_thread::sleep_until(tp);std::cout << "done\n";return 0;
}
二、mutex/lock_guard/unique_lock
(一)互斥量/RAII智能锁
类 / 接口 | 作用 | 常用构造 | 常用成员 | 特点 |
---|
std::mutex | 原始互斥量 | std::mutex m; | lock() / unlock() / try_lock() | 最底层,需手动解锁 |
std::lock_guard | 轻量 RAII 锁 | lock_guard<mutex> lg(m); | 无(不可拷贝不可移动) | 构造加锁,析构解锁,简单高效 |
std::unique_lock | 全能 RAII 锁 | unique_lock<mutex> ul(m); | lock() / unlock() / try_lock() / try_lock_for() / release() | 可延迟锁、可反复锁、可条件变量配合 |
(二)常用接口
接口 | 一句话 |
---|
mutex::lock() | 阻塞直到拿到锁 |
mutex::try_lock() | 拿不到立即返回 false |
mutex::unlock() | 释放锁 |
lock_guard | 构造即锁,析构即解,不能中途解 |
unique_lock | 想锁就锁,想解就解,还能给 condition_variable 用 |
(三)用法介绍
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>std::mutex g_mtx;
int g_count = 0;void worker(int id)
{{std::lock_guard<std::mutex> lg(g_mtx);++g_count;std::cout << "thread " << id << " count=" << g_count << '\n';} std::unique_lock<std::mutex> ul(g_mtx, std::defer_lock); ul.lock(); g_count += 10;std::cout << "thread " << id << " add 10 -> " << g_count << '\n';ul.unlock(); std::timed_mutex tmtx; if (ul.try_lock_for(std::chrono::milliseconds(100))) {std::cout << "thread " << id << " got timed_lock\n";ul.unlock();} else {std::cout << "thread " << id << " timeout\n";}
}int main()
{std::thread t1(worker, 1);std::thread t2(worker, 2);t1.join();t2.join();std::cout << "final count = " << g_count << '\n';return 0;
}
三、时间命名空间chrono的一些成员
(一)两个重要的类
类 | 头文件 | 作用 | 常用别名 | 常用接口 |
---|
std::chrono::duration<Rep, Period> | <chrono> | 表示“时间长度” | seconds , milliseconds , microseconds , nanoseconds , minutes , hours | count() , + , - , * , / , % , std::chrono::duration_cast<Dst>(d) |
std::chrono::time_point<Clock, Duration> | <chrono> | 表示“时间点” | system_clock::time_point , steady_clock::time_point , high_resolution_clock::time_point | time_since_epoch() , + , - , std::chrono::time_point_cast<Dst>(tp) |
(二)具体用法
#include <iostream>
#include <chrono>
#include <thread>int main()
{using namespace std::chrono;seconds s(5); milliseconds ms(1500); microseconds us = 2 * ms; std::cout << "ms.count() = " << ms.count() << '\n'; std::cout << "us.count() = " << us.count() << '\n'; seconds s2 = duration_cast<seconds>(ms); std::cout << "s2.count() = " << s2.count() << '\n'; steady_clock::time_point t0 = steady_clock::now();std::this_thread::sleep_for(1s);steady_clock::time_point t1 = steady_clock::now();auto elapsed = t1 - t0; std::cout << "elapsed ms = "<< duration_cast<milliseconds>(elapsed).count() << '\n'; auto deadline = steady_clock::now() + 500ms;std::cout << "deadline epoch = " << deadline.time_since_epoch().count() << '\n';return 0;
}
#include <iostream>
#include <ratio>
#include <chrono>int main ()
{using namespace std::chrono;typedef duration<int, std::ratio<60 * 60 * 24>> days_type;time_point<system_clock, days_type> today =time_point_cast<days_type>(system_clock::now());std::cout << today.time_since_epoch().count()<< " days since epoch\n";return 0;
}
四、atomic
(一)std::atomic 核心接口(store/load/exchange/CAS)
1、接口介绍
接口 | 一句话作用 | 默认内存序 | 一行代码示例 | 易错提醒 |
---|
store | 原子写 | seq_cst | a.store(7, memory_order_release); | 无返回值,纯写 |
load | 原子读 | seq_cst | int x = a.load(memory_order_acquire); | 永远返回副本 |
exchange | 拿旧值→换新值 | seq_cst | bool old = flag.exchange(true); | 适合"抢锁" |
compare_exchange_weak | 循环CAS(可假性失败) | seq_cst | while(!a.compare_exchange_weak(exp, exp+1)); | 必须包while |
compare_exchange_strong | 单次CAS | seq_cst | ok = a.compare_exchange_strong(exp, 100); | 别放循环里 |
2、实例
store / load
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>std::atomic<int> value{0};int main(){std::vector<std::thread> v;for(int i=1;i<=10;++i)v.emplace_back([i]{ value.store(i, std::memory_order_relaxed); });for(auto& t:v) t.join();std::cout << "final value = " << value.load(std::memory_order_relaxed) << '\n';
}
exchange —— 实现自旋锁
#pragma once
#include <atomic>class spinlock {std::atomic<bool> flag_{false};
public:void lock() noexcept {while (flag_.exchange(true, std::memory_order_acquire)) {}}void unlock() noexcept {flag_.store(false, std::memory_order_release);}class guard {spinlock& lk_;public:explicit guard(spinlock& lk) : lk_(lk) { lk_.lock(); }~guard() { lk_.unlock(); }guard(const guard&) = delete;guard& operator=(const guard&) = delete;};
};
compare_exchange_weak
#include <atomic>
#include <thread>
#include <iostream>std::atomic<long> counter{0};void inc(){long old = counter.load(std::memory_order_relaxed);while(!counter.compare_exchange_weak(old, old + 1,std::memory_order_relaxed,std::memory_order_relaxed)){ }
}int main(){std::thread t[4];for(auto& th:t) th = std::thread([]{ for(int i=0;i<250000;++i) inc(); });for(auto& th:t) th.join();std::cout << "counter = " << counter.load() << '\n';
}
compare_exchange_strong
#include <atomic>
#include <thread>
#include <iostream>std::atomic<int*> ptr{nullptr};int* get_singleton(){int* p = ptr.load(std::memory_order_acquire);if(!p){int* tmp = new int(42);if(ptr.compare_exchange_strong(p, tmp, std::memory_order_release))p = tmp; elsedelete tmp; }return p;
}int main(){std::thread t1(get_singleton), t2(get_singleton);t1.join(); t2.join();std::cout << *ptr.load() << '\n';
}
compare_exchange_strong
#include <atomic>
#include <thread>
#include <iostream>std::atomic<int*> ptr{nullptr};int* get_singleton(){int* p = ptr.load(std::memory_order_acquire);if(!p){int* tmp = new int(42);if(ptr.compare_exchange_strong(p, tmp, std::memory_order_release))p = tmp; elsedelete tmp; }return p;
}int main(){std::thread t1(get_singleton), t2(get_singleton);t1.join(); t2.join();std::cout << *ptr.load() << '\n';
}
(二)memory_order
1、为什么需要 memory_order
你可能以为代码是按顺序执行的,但实际上:
编译器和 CPU 会在不破坏单线程行为的前提下,重排指令。
这在多线程环境下会导致可见性问题:
一个线程写完数据,另一个线程却“看不见”。
2、六种meory_order选项
内存顺序 | 作用简述 |
---|
memory_order_relaxed | 只保证原子性,不保证顺序(最快,最危险) |
memory_order_consume | 已废弃,不建议使用 |
memory_order_acquire | 加载时使用,禁止后续操作被重排到加载之前 |
memory_order_release | 存储时使用,禁止前面操作被重排到存储之后 |
memory_order_acq_rel | 读改写操作使用,兼具 acquire + release |
memory_order_seq_cst | 最强顺序保证,所有线程看到一致的操作顺序(默认,最慢) |
下面演示最常用有效的两种用法
memory_order_acquire
和memory_order_release
#include <atomic>
#include <thread>
#include <cassert>std::atomic<int> ready{0};
int data = 0;void writer() {data = 42; ready.store(1, std::memory_order_release);
}void reader() {while (ready.load(std::memory_order_acquire) == 0) {}assert(data == 42);
}int main() {std::thread t1(writer);std::thread t2(reader);t1.join();t2.join();return 0;
}
❌ 错误示范:使用 relaxed
void writer() {data = 42;ready.store(1, std::memory_order_relaxed);
}
可能结果:reader 看到 ready == 1,但 data 还是 0!
原因:data = 42 被重排到 ready.store(1) 之后。
memory_order_seq_cst
优点:所有线程看到的操作顺序一致,无脑安全。
缺点:性能开销最大,不适合高性能场景。
实战建议
场景 | 推荐 memory_order |
---|
简单计数器(无同步需求) | relaxed |
线程间传递数据(如 flag + 数据) | release + acquire |
读改写操作(如 fetch_add) | acq_rel |
不想动脑,追求绝对正确 | seq_cst (默认) |
五、conditon_variable
实际就是对环境变量的一个封装。

std::condition_variable::wait 的带谓词版本(wait(lock, pred)),它是 C++ 标准库为“安全等待”提供的语法糖,用来一键解决两个经典问题:
- 虚假唤醒(spurious wake-up)
- 条件尚未成立就往下跑
cv.wait(lck);
cv.wait(lck, pred);
可以传入lamda表达式
cv.wait(lock, []{ return !q.empty(); });
接口 | 作用 | 阻塞条件 | 返回 |
---|
wait() | 阻塞直至被通知 | flag == false | void |
wait_for(ms) | 阻塞或超时 | flag == false && 未超时 | true=触发, false=超时 |
notify() | 唤醒 1 个等待线程 | — | void |
notify_all() | 唤醒 全部 等待线程 | — | void |
六、异步操作常用接口
名称 | 头文件 | 角色 | 典型接口 |
---|
std::async | <future> | 一键启动异步任务 | async(launch::async, func, args…) |
std::promise | <future> | “写端”——给异步线程一个“交货”通道 | set_value / set_exception / get_future |
std::future | <future> | “读端”——在主线程安全地拿结果 | get / wait / wait_for / valid |
(一)std::async
策略 | 标准措辞 | 实际语义(主流实现) | 性能/备注 |
---|
launch::async | “as if in a new thread” | 必定创建新线程(或线程池线程)并在其中执行 | 最可预期,开销最大 |
launch::deferred | “deferred until get() /wait() ” | 惰性调用;不创建新线程;首次 get() 才同步执行 | 零线程开销,但可能拖慢关键路径 |
launch::async | launch::deferred | 实现可任选上面两种 | 这是版本(1)的默认策略! GNU/libc++ 普遍按“线程池满则用 deferred”策略 | 行为不确定,写库时永远不要依赖 |
#include <iostream>
#include <future>
#include <thread>
#include <chrono>using namespace std::chrono_literals;
using clk = std::chrono::steady_clock;bool is_prime(long long x)
{std::cout << "[TID=" << std::this_thread::get_id() << "] start calc\n";for (long long i = 2; i * i <= x; ++i)if (x % i == 0) return false;return true;
}void test(const char* name, std::launch policy)
{std::cout << "\n----- " << name << " -----\n";auto t0 = clk::now();std::future<bool> fut;if (policy == (std::launch::async | std::launch::deferred))fut = std::async(is_prime, 982451653LL); elsefut = std::async(policy, is_prime, 982451653LL);std::this_thread::sleep_for(200ms); std::cout << "main doing something else...\n";bool ok = fut.get();auto ms = std::chrono::duration<double, std::milli>(clk::now() - t0).count();std::cout << "result=" << ok << " total=" << ms << " ms\n";
}int main()
{test("launch::async", std::launch::async);test("launch::deferred", std::launch::deferred);test("default (async|deferred)", std::launch::async | std::launch::deferred);return 0;
}
(二)std::promise
promise 只能读取一次,设置一次,如果需要“多次读取”怎么办?
用 std::shared_future
,它支持 任意多次 get(),每次返回 const T&:
接口 | 语义 | 备注 |
---|
构造函数 | promise() / promise(std::allocator_arg_t, Alloc) | 新建共享状态 |
promise(promise&&) noexcept | 移动构造 | 原对象置空 |
promise(const promise&) = delete | 禁止拷贝 | — |
赋值 | promise& operator=(promise&&) noexcept | 移动赋值 |
析构 | ~promise() | 若共享状态未就绪,存储 broken_promise 异常 |
get_future() | 一次性生成 std::future | 重复调用抛 future_error |
set_value(const T&) / set_value(T&&) | 写入结果 | 只能调一次 |
set_exception(std::exception_ptr) | 写入异常 | 与 set_value 二选一 |
set_value_at_thread_exit / set_exception_at_thread_exit | 延迟到线程退出才就绪 | 用于“线程局部对象先析构”场景 |
#include <iostream>
#include <thread>
#include <future>
#include <exception>using namespace std::chrono_literals;void worker(std::promise<int> prom)
{try{std::this_thread::sleep_for(1s);prom.set_value(42); }catch (...){prom.set_exception(std::current_exception()); }
}int main()
{std::promise<int> prom; std::future<int> fut = prom.get_future(); std::thread th(worker, std::move(prom)); std::cout << "waiting...\n";try{int val = fut.get(); std::cout << "got: " << val << '\n';}catch (const std::exception& ex){std::cout << "exception: " << ex.what() << '\n';}th.join();return 0;
}
(三)future / shared_future
方法 | 描述 | 返回值 |
---|
get() | 获取异步操作结果(仅一次) | T 或 void |
valid() | 是否拥有共享状态 | bool |
wait() | 阻塞到结果就绪 | void |
wait_for(rel_time) | 等待一段相对时间 | future_status |
wait_until(abs_time) | 等待到绝对时间点 | future_status |
share() | 升级为可多次 get 的 shared_future | std::shared_future<T> |
#include <iostream>
#include <future>
#include <thread>
#include <chrono>using namespace std::chrono_literals;int compute() { std::this_thread::sleep_for(1s); return 42; }int main() {std::future<int> fut = std::async(std::launch::async, compute);std::cout << "valid: " << fut.valid() << '\n'; if (fut.wait_for(500ms) == std::future_status::timeout)std::cout << "not ready yet\n";fut.wait(); std::cout << "result: " << fut.get() << '\n'; std::cout << "valid after get: " << fut.valid() << '\n'; return 0;
}
使用shared_future多次获取。
#include <iostream>
#include <future>
#include <thread>std::string download(const std::string& url) {std::this_thread::sleep_for(std::chrono::milliseconds(500));return "data from " + url;
}int main() {std::promise<std::string> prom;std::shared_future<std::string> sf = prom.get_future().share(); std::thread t1([sf] { std::cout << "t1: " << sf.get() << '\n'; });std::thread t2([sf] { std::cout << "t2: " << sf.get() << '\n'; });prom.set_value("https://example.com"); t1.join(); t2.join();return 0;
}
(四)package_task
接口 | 说明 | 备注 |
---|
packaged_task<R(Args...)> | 构造函数,包装任意可调用目标 | 支持函数、lambda、bind、函数对象 |
packaged_task(packaged_task&&) noexcept | 移动构造 / 移动赋值 | 不可拷贝 |
~packaged_task() | 析构 | 未就绪则共享状态存 broken_promise |
valid() | 是否拥有共享状态 | 默认构造或移动后可能为 false |
get_future() | 取出关联的 std::future<R> | 只能调用一次 |
operator()(Args...) | 同步执行任务,结果写入共享状态 | 可重复调用(需 reset() ) |
reset() | 重置共享状态,可再次 get_future() | 原状态若未就绪则置 broken_promise |
swap(packaged_task&) | 交换底层状态 | noexcept |
#include <iostream>
#include <future>
#include <thread>int add(int a, int b) { return a + b; }int main() {std::packaged_task<int(int, int)> task(add); std::future<int> fut = task.get_future(); task(7, 8); std::cout << "result = " << fut.get() << '\n'; return 0;
}
可以包装lamda
以及bind
auto task_lambda = std::packaged_task<double(double, double)>([](double x, double y) { return std::pow(x, y); });auto task_bind = std::packaged_task<int()>(std::bind([](int x, int y){ return x * y; }, 9, 9));