C++ STL之std::thread与异步编程详解
一、用法速查
1.1 std::thread------基础线程
std::thread 是 C++11 引入的线程抽象,构造即启动,需在销毁前明确 join 或 detach ,否则析构时触发 std::terminate。
cpp
#include <thread>
#include <iostream>
void work(int id) {
std::cout << "thread " << id << " running\n";
}
int main() {
std::thread t(work, 1);
t.join(); // 等待线程结束(必须调用)
// t.detach(); // 或者分离(后台运行)
return 0;
}
RAII 包装(推荐):用对象生命周期保证 join:
cpp
class ThreadGuard {
std::thread t;
public:
explicit ThreadGuard(std::thread t_) : t(std::move(t_)) {}
~ThreadGuard() { if (t.joinable()) t.join(); }
ThreadGuard(const ThreadGuard&) = delete;
ThreadGuard& operator=(const ThreadGuard&) = delete;
};
1.2 std::async------异步任务
std::async 返回 std::future,自动管理线程生命周期:
cpp
#include <future>
int calc(int x) { return x * x; }
int main() {
// launch::async → 立即新线程执行
auto f1 = std::async(std::launch::async, calc, 10);
// launch::deferred → 惰性求值,get() 时才同步执行
auto f2 = std::async(std::launch::deferred, calc, 20);
// 默认策略(= async | deferred)由实现自行选择
auto f3 = std::async(calc, 30);
std::cout << f1.get() << ' ' << f2.get() << ' ' << f3.get() << '\n';
return 0;
}
1.3 std::future / std::shared_future------获取结果
future 只能 get() 一次;shared_future 可多次获取。
cpp
std::promise<int> p;
auto f = p.get_future();
std::thread t([&p] { p.set_value(42); });
std::cout << f.get() << '\n'; // 阻塞直到拿到值
t.join();
1.4 std::packaged_task------包装可调用对象
将函数包装为异步任务,与 future 关联:
cpp
std::packaged_task<int(int, int)> task([](int a, int b) { return a + b; });
auto fut = task.get_future();
std::thread t(std::move(task), 3, 4);
t.join();
std::cout << fut.get() << '\n'; // 7
二、底层原理
2.1 future-promise 值通道
promise 与 future 共享一个内部状态(shared state),通过条件变量实现线程间同步:
#mermaid-svg-ggyFLBPpYc86OOGd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ggyFLBPpYc86OOGd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ggyFLBPpYc86OOGd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ggyFLBPpYc86OOGd .error-icon{fill:#552222;}#mermaid-svg-ggyFLBPpYc86OOGd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ggyFLBPpYc86OOGd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ggyFLBPpYc86OOGd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ggyFLBPpYc86OOGd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ggyFLBPpYc86OOGd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ggyFLBPpYc86OOGd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ggyFLBPpYc86OOGd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ggyFLBPpYc86OOGd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ggyFLBPpYc86OOGd .marker.cross{stroke:#333333;}#mermaid-svg-ggyFLBPpYc86OOGd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ggyFLBPpYc86OOGd p{margin:0;}#mermaid-svg-ggyFLBPpYc86OOGd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ggyFLBPpYc86OOGd .cluster-label text{fill:#333;}#mermaid-svg-ggyFLBPpYc86OOGd .cluster-label span{color:#333;}#mermaid-svg-ggyFLBPpYc86OOGd .cluster-label span p{background-color:transparent;}#mermaid-svg-ggyFLBPpYc86OOGd .label text,#mermaid-svg-ggyFLBPpYc86OOGd span{fill:#333;color:#333;}#mermaid-svg-ggyFLBPpYc86OOGd .node rect,#mermaid-svg-ggyFLBPpYc86OOGd .node circle,#mermaid-svg-ggyFLBPpYc86OOGd .node ellipse,#mermaid-svg-ggyFLBPpYc86OOGd .node polygon,#mermaid-svg-ggyFLBPpYc86OOGd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ggyFLBPpYc86OOGd .rough-node .label text,#mermaid-svg-ggyFLBPpYc86OOGd .node .label text,#mermaid-svg-ggyFLBPpYc86OOGd .image-shape .label,#mermaid-svg-ggyFLBPpYc86OOGd .icon-shape .label{text-anchor:middle;}#mermaid-svg-ggyFLBPpYc86OOGd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ggyFLBPpYc86OOGd .rough-node .label,#mermaid-svg-ggyFLBPpYc86OOGd .node .label,#mermaid-svg-ggyFLBPpYc86OOGd .image-shape .label,#mermaid-svg-ggyFLBPpYc86OOGd .icon-shape .label{text-align:center;}#mermaid-svg-ggyFLBPpYc86OOGd .node.clickable{cursor:pointer;}#mermaid-svg-ggyFLBPpYc86OOGd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ggyFLBPpYc86OOGd .arrowheadPath{fill:#333333;}#mermaid-svg-ggyFLBPpYc86OOGd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ggyFLBPpYc86OOGd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ggyFLBPpYc86OOGd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ggyFLBPpYc86OOGd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ggyFLBPpYc86OOGd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ggyFLBPpYc86OOGd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ggyFLBPpYc86OOGd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ggyFLBPpYc86OOGd .cluster text{fill:#333;}#mermaid-svg-ggyFLBPpYc86OOGd .cluster span{color:#333;}#mermaid-svg-ggyFLBPpYc86OOGd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ggyFLBPpYc86OOGd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ggyFLBPpYc86OOGd rect.text{fill:none;stroke-width:0;}#mermaid-svg-ggyFLBPpYc86OOGd .icon-shape,#mermaid-svg-ggyFLBPpYc86OOGd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ggyFLBPpYc86OOGd .icon-shape p,#mermaid-svg-ggyFLBPpYc86OOGd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ggyFLBPpYc86OOGd .icon-shape .label rect,#mermaid-svg-ggyFLBPpYc86OOGd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ggyFLBPpYc86OOGd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ggyFLBPpYc86OOGd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ggyFLBPpYc86OOGd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} set_value / set_exception
get() 等待并读取
promise
(生产者)
共享状态
shared state
future
(消费者)
线程 A
线程 B
- 生产者调用
set_value()时,共享状态标记 ready 并唤醒等待者。 - 消费者调用
get()时,若未 ready 则阻塞在条件变量上。 future析构时释放共享状态,shared_future允许多读。
2.2 async 两种启动策略
#mermaid-svg-ug7udNyjqz45qB4X{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ug7udNyjqz45qB4X .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ug7udNyjqz45qB4X .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ug7udNyjqz45qB4X .error-icon{fill:#552222;}#mermaid-svg-ug7udNyjqz45qB4X .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ug7udNyjqz45qB4X .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ug7udNyjqz45qB4X .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ug7udNyjqz45qB4X .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ug7udNyjqz45qB4X .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ug7udNyjqz45qB4X .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ug7udNyjqz45qB4X .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ug7udNyjqz45qB4X .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ug7udNyjqz45qB4X .marker.cross{stroke:#333333;}#mermaid-svg-ug7udNyjqz45qB4X svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ug7udNyjqz45qB4X p{margin:0;}#mermaid-svg-ug7udNyjqz45qB4X .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ug7udNyjqz45qB4X .cluster-label text{fill:#333;}#mermaid-svg-ug7udNyjqz45qB4X .cluster-label span{color:#333;}#mermaid-svg-ug7udNyjqz45qB4X .cluster-label span p{background-color:transparent;}#mermaid-svg-ug7udNyjqz45qB4X .label text,#mermaid-svg-ug7udNyjqz45qB4X span{fill:#333;color:#333;}#mermaid-svg-ug7udNyjqz45qB4X .node rect,#mermaid-svg-ug7udNyjqz45qB4X .node circle,#mermaid-svg-ug7udNyjqz45qB4X .node ellipse,#mermaid-svg-ug7udNyjqz45qB4X .node polygon,#mermaid-svg-ug7udNyjqz45qB4X .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ug7udNyjqz45qB4X .rough-node .label text,#mermaid-svg-ug7udNyjqz45qB4X .node .label text,#mermaid-svg-ug7udNyjqz45qB4X .image-shape .label,#mermaid-svg-ug7udNyjqz45qB4X .icon-shape .label{text-anchor:middle;}#mermaid-svg-ug7udNyjqz45qB4X .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ug7udNyjqz45qB4X .rough-node .label,#mermaid-svg-ug7udNyjqz45qB4X .node .label,#mermaid-svg-ug7udNyjqz45qB4X .image-shape .label,#mermaid-svg-ug7udNyjqz45qB4X .icon-shape .label{text-align:center;}#mermaid-svg-ug7udNyjqz45qB4X .node.clickable{cursor:pointer;}#mermaid-svg-ug7udNyjqz45qB4X .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ug7udNyjqz45qB4X .arrowheadPath{fill:#333333;}#mermaid-svg-ug7udNyjqz45qB4X .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ug7udNyjqz45qB4X .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ug7udNyjqz45qB4X .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ug7udNyjqz45qB4X .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ug7udNyjqz45qB4X .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ug7udNyjqz45qB4X .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ug7udNyjqz45qB4X .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ug7udNyjqz45qB4X .cluster text{fill:#333;}#mermaid-svg-ug7udNyjqz45qB4X .cluster span{color:#333;}#mermaid-svg-ug7udNyjqz45qB4X div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ug7udNyjqz45qB4X .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ug7udNyjqz45qB4X rect.text{fill:none;stroke-width:0;}#mermaid-svg-ug7udNyjqz45qB4X .icon-shape,#mermaid-svg-ug7udNyjqz45qB4X .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ug7udNyjqz45qB4X .icon-shape p,#mermaid-svg-ug7udNyjqz45qB4X .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ug7udNyjqz45qB4X .icon-shape .label rect,#mermaid-svg-ug7udNyjqz45qB4X .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ug7udNyjqz45qB4X .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ug7udNyjqz45qB4X .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ug7udNyjqz45qB4X :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} launch::async
launch::deferred
std::async 调用
启动策略
在新线程上
立即执行
存入共享状态
延迟执行
future.get()
可能直接返回
future.get()
触发同步计算
销毁共享状态
launch::async 创建独立线程立即执行;launch::deferred 仅在 get() 或 wait() 时在当前线程同步调用------即惰性求值。
2.3 线程池原理与实现骨架
每次新建 std::thread 涉及系统调用,线程池通过复用线程减少开销。核心组件:
- 任务队列 :
std::queue<std::packaged_task<void()>> - 工作线程:循环从队列取任务执行
- 同步原语 :
std::mutex+std::condition_variable - 停止标志 :
std::atomic<bool>
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 n) {
for (size_t i = 0; i < n; ++i)
workers.emplace_back([this] {
while (true) {
std::function<void()> task;
{
std::unique_lock 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, class... Args>
auto enqueue(F&& f, Args&&... args) -> std::future<decltype(f(args...))> {
using Ret = decltype(f(args...));
auto pkg = std::make_shared<std::packaged_task<Ret()>>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
auto res = pkg->get_future();
{
std::lock_guard lock(mtx);
tasks.emplace([pkg] { (*pkg)(); });
}
cv.notify_one();
return res;
}
~ThreadPool() {
{
std::lock_guard lock(mtx);
stop = true;
}
cv.notify_all();
for (auto& w : workers) w.join();
}
};
三、面试题
Q1:std::thread 析构时如果既没 join 也没 detach 会怎样?
调用
std::terminate。C++ 标准强制要求销毁前必须明确线程的处理方式,防止资源泄漏。
Q2:std::async 默认策略是什么?有什么陷阱?
默认是
launch::async | launch::deferred,由实现自行选择。陷阱:某些实现(如 MSVC)对默认策略启用延迟求值 ,导致future析构时阻塞等待结果("阻塞析构"),可能引发死锁。
Q3:std::promise 和 std::packaged_task 的异同?
两者都通过共享状态连接生产者与消费者。
promise是值通道 ------手动 set_value/set_exception;packaged_task是函数包装------自动把返回值或异常写入共享状态。
Q4:std::future 为什么只能 get() 一次?
get()内部将共享状态的所有权转移给调用者(move语义),调用后valid() == false。需要多次取值的场景用std::shared_future。
Q5:线程池为什么比每次 new thread 快?
系统线程创建/销毁涉及内核态切换、栈分配和调度器注册,每次约数微秒。线程池复用 N 个工作线程,消除重复创建开销,且通过任务队列削峰填谷,避免线程风暴。
Q6:std::launch::deferred 的典型应用场景?
惰性求值。当计算结果可能不会被用到(如条件分支中的一路),或需要将计算推迟到确定线程上下文中时,deferred 策略避免不必要的线程开销。
Q7:notify_one 和 notify_all 的区别?线程池中为什么 stop 时用 notify_all?
notify_one唤醒一个等待线程;notify_all唤醒全部。线程池 stop 时所有工作线程都应退出,必须用notify_all,否则部分线程可能永久阻塞。