std::stop_token是C++20引入的协作式线程取消机制核心组件,用于让线程主动检查停止请求并安全退出,解决传统join阻塞和裸布尔标志导致的竞态与漏检问题。

std::stop_token 是什么,它能解决什么问题
std::stop_token 是 C++20 引入的协作式线程取消机制核心组件,不是“强制杀线程”,而是让工作线程主动检查是否被请求停止,并自行清理后退出。它解决的是传统 std::thread::join() 阻塞等待、或用裸布尔标志(volatile bool running)导致竞态和漏检的问题。
常见错误现象包括:
- 工作循环中没检查停止请求,线程卡死无法退出
- 多次调用
request_stop()导致未定义行为(只能调一次) -
std::stop_token和std::stop_source生命周期错配(比如stop_source提前析构)
如何正确构造并传递 stop_token 到工作线程
关键点是:每个线程应拥有自己的 std::stop_source(可选),但通常共享一个;std::stop_token 可拷贝,且轻量,适合传值给线程函数。
#include <thread>
#include <stop_token>
#include <chrono>
void worker(std::stop_token stoken) {
while (!stoken.stop_requested()) {
// 模拟工作
std::this_thread::sleep_for(100ms);
// 注意:这里不能只靠 sleep 退出 —— 必须显式检查
}
}
int main() {
std::stop_source ssource;
auto t = std::thread(worker, ssource.get_token());
std::this_thread::sleep_for(500ms);
ssource.request_stop(); // 只能调一次
t.join();
}使用场景注意:
立即学习“C++免费学习笔记(深入)”;
-
std::stop_token必须在工作函数参数中显式接收(不能靠全局/静态变量) - 若线程内需注册回调(如异步 I/O 取消),要用
std::stop_callback,它依赖stop_token的有效性 - 不要在线程外保存
std::stop_token并长期持有——它不保证底层状态存活,只反映当时关联的stop_source状态
在阻塞系统调用中如何响应 stop_token
纯忙等(while(!token.stop_requested()))浪费 CPU;但像 std::condition_variable::wait、std::future::wait 这类阻塞点,必须配合带 stop_token 的重载版本,否则会完全忽略停止请求。
例如:
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker(std::stop_token stoken) {
std::unique_lock<std::mutex> lk(mtx);
// ✅ 正确:wait 会响应 stop_token 并返回 cv_status::no_timeout 或 cv_status::timeout
while (!ready && cv.wait(lk, stoken, []{ return ready; }) == std::cv_status::no_timeout) {
// 条件满足或被 stop 请求唤醒
}
}容易踩的坑:
- 错误写法:
cv.wait(lk)(无 token)→ 完全无视停止请求 -
std::this_thread::sleep_for不接受stop_token,必须手动轮询 +stop_requested() - Windows 上某些 API(如
WaitForSingleObject)没有原生 stop_token 支持,需封装为可中断等待(例如用WaitForMultipleObjects等待事件 + stop_event)
stop_callback 的适用边界与资源泄漏风险
std::stop_callback 在 stop_source 被请求停止时自动触发,常用于清理非 RAII 资源(如释放句柄、注销回调)。但它有隐含约束:
- 回调对象生命周期必须长于
stop_callback实例(通常用 lambda 捕获局部变量极易出错) - 回调执行期间,
stop_source仍有效,但其他线程可能已开始析构相关资源
void worker(std::stop_source ssource) {
HANDLE h = CreateEvent(nullptr, true, false, nullptr);
// ✅ 安全:lambda 不捕获 h,只用值拷贝
std::stop_callback cb(ssource.get_token(), [h]{
CloseHandle(h); // h 是值,没问题
});
// ❌ 危险:捕获引用或 this,可能已析构
// std::stop_callback cb(token, [&]{ cleanup(); });
}性能影响很小——stop_callback 构造是 O(1),触发是原子操作;但不要在 hot path 频繁创建销毁它。
兼容性注意:MSVC 19.30+、GCC 11+、Clang 12+ 支持完整 stop_token 语义;旧编译器需手动模拟。
真正麻烦的是跨模块协作:如果线程池、异步任务框架、第三方库各自维护 stop_source,就容易出现“谁该 request_stop”“token 是否还有效”的混乱。别指望它自动传播——每个线程的停止信号都得明确设计传递路径。


















