std::stop_token是C++20协作式线程取消机制的核心,提供轻量、无锁、可轮询的停止信号;它解决传统std::thread缺乏安全退出语义的问题,需配合std::jthread自动管理生命周期。

std::stop_token 是什么,它能解决什么问题
std::stop_token 是 C++20 引入的协作式线程取消机制的核心组件,它本身不“终止”线程,而是提供一种轻量、无锁、可轮询的信号传递方式。它解决的是传统 std::thread 缺乏安全退出语义的问题——比如用布尔标志位容易因内存序或竞态导致漏检,而强制 std::thread::detach() 或 std::thread::join() 前未同步清理则可能 crash。
关键点在于:它必须配合 std::jthread(C++20 新增)才能自动注册和触发停止请求;单独用 std::thread + std::stop_token 需手动管理 std::stop_source,容易出错。
如何用 std::jthread + stop_token 实现安全退出
std::jthread 在析构时会自动调用 request_stop() 并等待线程结束,这是“优雅终止”的基础。它的构造函数会绑定一个 std::stop_source,并将对应 std::stop_token 传入线程函数。
void worker(std::stop_token stoken) {
while (!stoken.stop_requested()) {
// 模拟工作:读队列、处理数据、sleep 等
if (auto item = pop_work(); item.has_value()) {
process(*item);
} else {
std::this_thread::sleep_for(10ms);
}
}
// 此处确保 cleanup 可执行:释放资源、关闭文件、注销回调等
cleanup();
}
<p>int main() {
std::jthread t{worker}; // 自动构造 stop_source + token
// ... 主线程做其他事
// t 析构时自动 request_stop() 并 join()
}</p>-
std::jthread构造时若传入可调用对象,会自动创建关联的std::stop_source - 线程函数参数必须是
std::stop_token类型(或 const 引用),否则无法接收 token - 不要在循环中频繁调用
stoken.stop_requested()—— 它是 cheap,但若工作逻辑本身已含阻塞点(如queue.pop_wait(stoken)),应优先使用支持 stop_token 的阻塞调用(见下节)
哪些标准库函数支持 stop_token?别硬轮询
C++20 起,部分标准库阻塞操作开始接受 std::stop_token 参数,让等待能被主动中断,避免“轮询 + sleep”这种低效且易延迟的模式。
立即学习“C++免费学习笔记(深入)”;
支持的典型场景包括:
-
std::condition_variable::wait(..., std::stop_token):替代 while 循环 +notify_one()手动唤醒 -
std::mutex::try_lock_until(..., std::stop_token)(注意:不是所有实现都完整支持,libc++ 和 MSVC 较全,libstdc++ 13+ 开始逐步补全) - 第三方库如
moodycamel::BlockingConcurrentQueue::wait_dequeue_timed(..., std::stop_token)(需确认版本)
例如用条件变量等待任务:
std::mutex mtx;
std::condition_variable cv;
std::queue<Task> tasks;
<p>void worker(std::stop_token stoken) {
std::unique_lock<std::mutex> lk(mtx);
while (!stoken.stop_requested()) {
cv.wait(lk, stoken, [&]{ return !tasks.empty(); }); // ✅ 支持 stop_token 的 wait
if (!tasks.empty()) {
auto t = std::move(tasks.front()); tasks.pop();
lk.unlock();
t();
lk.lock();
}
}
}</p>- 必须用
cv.wait(lock, stoken, pred)形式,不能只传stoken;第三个参数谓词仍需存在,否则编译失败 - 若谓词返回 false 且
stoken.stop_requested() == true,wait会立即返回,此时应退出循环 - 不要混用
cv.wait_for(lk, 100ms)和手动检查stoken—— 这样在 100ms 内无法响应停止请求
常见错误:stop_token 失效或提前失效
std::stop_token 的生命周期必须覆盖整个线程执行期,否则 stop_requested() 永远返回 false,或访问已销毁对象。
- 错误:把
std::stop_source存在局部作用域,然后把它的get_token()传给线程
→std::stop_token引用的内部状态随stop_source析构而失效,后续调用行为未定义 - 错误:在线程函数里拷贝
std::stop_token后又 std::move 它
→std::stop_token是可复制、不可移动的类型,std::move后仍有效,但语义混乱,毫无必要 - 错误:多个线程共用同一个
std::stop_source,但只在一个线程里调用request_stop()
→ 其他线程的stop_token也会收到通知,这是设计使然;但若误以为“只影响本线程”,可能造成意外退出
真正需要注意的边界是:如果线程函数捕获了外部 std::stop_source 的引用或指针,并在其 lifetime 结束后还尝试调用 request_stop(),会导致未定义行为。所以优先用 std::jthread,它把生命周期管理收束到单一对象内。
std::jthread 的析构行为看似简单,但背后依赖 std::stop_source 的 RAII 和原子状态同步,稍有不慎(比如提前 move 走 jthread 对象又忘了 detach)就会卡死或崩溃。实际项目里,建议封装一层带超时 join 的 wrapper,而不是裸用 std::jthread。


















