Wiki
jthread 與 stop_token
本文由簡體中文內容確定性轉換,並受版本化術語表保護。
C++20 提供了:
std::jthread
它可以理解成一個更適合現代 C++ 的執行緒類。
和 std::thread 相比,它最重要的改進有兩個:
1. 生命周期更安全
→ 析构时自动处理线程,不容易因为漏写 join() 而出问题
2. 自带协作式停止机制
→ 可以通过 stop_token / request_stop() 请求线程安全退出
所以這一節最重要的是把兩個概念分開:
线程生命周期管理
→ 谁负责等待线程结束
线程停止协议
→ 谁发出停止请求,工作线程怎么响应
std::jthread 同時解決了這兩個問題,但它並不是“自動強殺執行緒”的工具。
1.為什麼需要 std::jthread
1.1.std::thread 的問題
使用普通 std::thread:
std::thread thread(work);
線上程物件析構之前,必須保證它已經:
thread.join();
或者:
thread.detach();
否則,如果一個仍然 joinable() 的 std::thread 被析構,程式會呼叫:
std::terminate();
例如:
#include <print>
#include <thread>
void work()
{
std::println("working");
}
int main()
{
std::thread thread(work);
// 如果这里忘记 join() / detach()
}
程式離開 main() 時,thread 仍然是 joinable 的,於是會觸發 std::terminate()。

真正麻煩的是,當程式中出現:
异常
提前 return
多个 if / else 分支
复杂资源管理
就更容易漏掉 join()。
這和手動管理 new/delete 有點像:
代码简单时看起来没问题
控制路径一复杂
→ 很容易忘记收尾
而現代 C++ 更傾向於通過 RAII 自動管理資源。
執行緒也一樣。
1.2.std::jthread 會自動處理執行緒結束
最簡單的例子:
#include <print>
#include <thread>
void work()
{
std::println("working");
}
int main()
{
std::jthread thread(work);
}
執行結果:
working

這裡沒有寫:
thread.join();
但程式仍然可以正常結束。
因為當:
std::jthread thread;
離開作用域時,如果它仍然是 joinable 的,解構函式會負責收尾。
可以先粗略理解成:
std::thread
→ 需要自己保证 join() / detach()
std::jthread
→ 析构时自动处理
不過 jthread 的析構不只是簡單地 join()。
更準確地說,如果它仍然 joinable,析構過程概念上會做:
request_stop()
↓
join()
也就是:
先請求執行緒停止,然後等待執行緒真正結束。
這也是 jthread 和普通 thread 一個很重要的區別。
1.3.jthread 不會強制殺死執行緒
std::jthread 使用的是:
協作式停止(cooperative cancellation)
所謂“協作式”,意思是:
某个线程:
发出停止请求
↓
工作线程:
看到停止请求
↓
工作线程:
自己选择一个安全的位置退出
而不是:
request_stop()
↓
操作系统立刻把工作线程强行杀掉
標準庫不會隨便把執行緒從某條機器指令中間直接掐死。
原因很簡單。
執行緒可能正在:
持有 mutex
修改共享数据
写文件
操作硬件
分配 / 释放资源
修改对象内部状态
如果在任意位置強制終止執行緒,很容易把程式狀態搞壞。
所以:
thread.request_stop();
更像是在告訴工作執行緒:
“你該停了,請在合適的位置自己退出。”
而不是:
“現在立刻把你殺掉。”
2.stop_token 停止協議
2.1.std::stop_token
2.1.1.stop_token 是什麼
std::stop_token 可以理解成:
停止請求的觀察端。
它本身不能發出停止請求,只能檢視:
有没有人要求我停止?
例如:
stop_token.stop_requested();
返回:
true → 已经有人请求停止
false → 目前还没有停止请求
jthread 內部有一個停止狀態,可以粗略理解成:
jthread / stop_source
↓
发出停止请求
共享停止状态
↑
查看停止请求
↑
stop_token
也就是說:
request_stop()
→ 写入“停止请求”
stop_requested()
→ 查看“停止请求”
2.2.jthread 可以自動傳入 stop_token
來看一個最典型的例子:
#include <chrono>
#include <print>
#include <stop_token>
#include <thread>
using namespace std::chrono_literals;
void worker(std::stop_token token)
{
while (!token.stop_requested())
{
std::println("working");
std::this_thread::sleep_for(100ms);
}
std::println("stopping");
}
int main()
{
std::jthread thread(worker);
std::this_thread::sleep_for(350ms);
thread.request_stop();
}
可能輸出:
working
working
working
working
stopping
這裡最特別的是:
void worker(std::stop_token token)
但建立執行緒時卻只寫了:
std::jthread thread(worker);
我們並沒有手動傳:
worker(token);
因為 std::jthread 發現這個函式可以接收 std::stop_token,於是會自動把自己的 stop token 傳進去。
可以近似理解成:
std::jthread thread(worker);
内部效果类似:
worker(jthread 自动提供的 stop_token);
這裡不可以顯式填引數,比如std::jthread thread(worker, thread.get_stop_token());,
這是非法的,原因非常直接:在執行建構函式引數時,thread 物件還沒有構造完成,所以你還不能呼叫thread.get_stop_token()。
所以這裡直接記住他會自動把自己的stop token傳入進去就行了,不需要我們手動傳入。
2.3.request_stop()
呼叫:
thread.request_stop();
表示:
向這個
jthread關聯的停止狀態發出停止請求。
它只負責“發請求”。
它不會:
强制终止线程
自动跳出 while
直接让 worker return
等待线程结束
真正決定什麼時候退出的是工作執行緒。
例如:
while (!token.stop_requested())
{
do_work();
}
每輪迴圈檢查:
token.stop_requested()
一旦發現停止請求,就退出迴圈。
整個流程是:
main
│
│ 创建 jthread
↓
worker 开始执行
│
│ while (!token.stop_requested())
│
│ working...
│
│ working...
│
↑
main 调用 request_stop()
│
↓
停止状态变成“已请求停止”
│
↓
worker 下一次检查 stop_requested()
│
↓
发现为 true
│
↓
退出循环
所以最重要的一句話是:
request_stop()只是請求停止,真正停止必須由工作執行緒自己配合。
2.4.request_stop() 的返回值
request_stop() 會返回一個 bool:
bool result = thread.request_stop();
可以粗略理解成:
true
→ 这次调用真的把状态从
“还没请求停止”
改成了
“已经请求停止”
false
→ 之前已经请求过停止
或者当前停止状态无法产生停止请求
普通業務程式碼經常不需要關心這個返回值。
直接寫:
thread.request_stop();
就可以。
2.5.stop_possible()
stop_token 還可以呼叫:
token.stop_possible();
它表示:
這個 token 是否關聯著一個“有可能收到停止請求”的停止狀態。
例如:
std::stop_token token;
這是一個預設構造的 token,它沒有關聯正常的停止狀態。
此時通常:
token.stop_possible() == false
token.stop_requested() == false
而 std::jthread 自動提供的 token 通常是:
stop_possible() == true
對於普通使用來說,最常用的還是:
stop_requested()
stop_possible() 更多是在需要判斷“這個 token 到底支不支援停止協議”時使用。
2.6.jthread 析構時到底發生什麼
假設:
{
std::jthread thread(worker);
}
執行到右花括號:
}
thread 開始析構。
如果它仍然是 joinable 的,那麼概念上會:
thread.request_stop();
↓
thread.join();
所以:
{
std::jthread thread(worker);
}
可以理解成退出作用域時自動做了:
thread.request_stop();
thread.join();
這也是為什麼這種程式碼可以正常工作:
#include <print>
#include <stop_token>
#include <thread>
void worker(std::stop_token token)
{
while (!token.stop_requested())
{
// work
}
std::println("stopped");
}
int main()
{
std::jthread thread(worker);
}
main() 結束時:
jthread 析构
↓
request_stop()
↓
worker 看到停止请求
↓
worker 退出
↓
join() 等待结束
↓
析构完成
2.7.但析構也不能“憑空讓執行緒退出”
下面這個執行緒完全不檢查 stop_token:
void worker(std::stop_token)
{
for (;;) //死循环,效率比while(true)高
{
}
}
然後:
int main()
{
std::jthread thread(worker);
}
離開 main() 時:
jthread 析构
↓
request_stop()
↓
停止请求确实发出了
↓
但是 worker 根本不检查
↓
for(;;) 继续运行
↓
析构函数接着 join()
↓
join() 一直等
程式就會一直卡在那裡。
因此:
jthread提供的是停止協議,不是強制終止能力。
所以對於長期執行的 worker,一般應該在合適的位置檢查:
token.stop_requested()
例如:
while (!token.stop_requested())
{
do_some_work();
}
而不是隻線上程開頭檢查一次:
if (token.stop_requested())
{
return;
}
// 后面运行几个小时都不再检查
2.8.阻塞操作也會影響停止速度
即使程式碼寫成:
while (!token.stop_requested())
{
blocking_operation();
}
也不代表執行緒一定能馬上停止。
假設:
blocking_operation();
一次會阻塞 30 秒。
那麼即使外部已經執行:
thread.request_stop();
工作執行緒也必須先從:
blocking_operation();
返回,才能進行下一輪:
token.stop_requested()
所以停止響應速度還取決於工作執行緒內部在幹什麼。
對於需要快速停止的執行緒,要考慮:
阻塞函数有没有超时
阻塞操作能不能被唤醒
能不能把长任务拆成多个小步骤
能不能在多个安全点检查 stop_requested()
例如:
while (!token.stop_requested())
{
do_small_step();
}
通常比:
while (!token.stop_requested())
{
do_one_huge_blocking_job();
}
更容易快速響應停止請求。
2.9.std::stop_source
前面說過:
stop_token
→ 观察停止请求
那麼誰負責發出停止請求?
除了 jthread.request_stop(),標準庫還提供:
std::stop_source
它可以理解成:
停止請求的控制端。
例如:
#include <print>
#include <stop_token>
int main()
{
std::stop_source source;
std::stop_token token = source.get_token();
std::println("{}", token.stop_requested());
source.request_stop();
std::println("{}", token.stop_requested());
}
執行結果:
false
true
整個關係可以理解成:
stop_source
│
│ request_stop()
↓
共享停止状态
↑
│ stop_requested()
stop_token
所以:
std::stop_source source;
負責:
发请求
而:
std::stop_token token = source.get_token();
負責:
看请求
2.10.多個 stop_token 可以觀察同一個停止狀態
例如:
std::stop_source source;
std::stop_token token1 = source.get_token();
std::stop_token token2 = source.get_token();
std::stop_token token3 = source.get_token();
這三個 token 都關聯到同一個停止狀態。
當:
source.request_stop();
之後:
token1.stop_requested()
token2.stop_requested()
token3.stop_requested()
都會變成:
true
所以 stop_source + stop_token 很適合表達:
一个管理者
↓
统一发出停止请求
↓
多个任务 / 对象
↓
一起观察同一个停止状态
2.11.std::stop_callback
有時候我們不想一直手動檢查:
token.stop_requested()
而是希望:
一旦停止請求出現,就自動執行某個操作。
可以使用:
std::stop_callback
例如:
#include <print>
#include <stop_token>
int main()
{
std::stop_source source;
std::stop_token token = source.get_token();
std::stop_callback callback(token, [] {
std::println("stop requested");
});
source.request_stop();
}
執行結果:
stop requested
也就是說:
request_stop()
↓
停止状态改变
↓
已经注册的 stop_callback 被调用
他如果是jthread呼叫的話,那麼順序是:
request_stop()
↓
同步执行 stop_callback
↓
request_stop() 返回
↓
join()
也就是說,callback 先於 join() 完成。std::jthread 的析構本身就是先呼叫 request_stop(),再呼叫 join()。
它很適合幹一些很輕量的事情,例如:
设置状态
唤醒某个等待对象
通知某个组件开始收尾
但不建議在 callback 裡面做:
复杂计算
长时间 IO
长时间阻塞
大量锁操作
因為停止路徑本身應該儘量簡單、可控。
還有一個細節:
如果註冊 callback 時,停止請求已經發生,那麼 callback 可能會在註冊過程中直接執行。
所以不要假設:
callback 一定会在未来某个时间
由另一个线程执行
3.jthread 的執行緒函式和生命週期操作
3.1.jthread 不一定非要使用 stop_token
std::jthread 也可以像普通 std::thread 一樣執行普通函式。
例如:
#include <print>
#include <thread>
void work(int value)
{
std::println("{}", value);
}
int main()
{
std::jthread thread(work, 42);
}
執行結果:
42
這裡:
void work(int value)
根本沒有 stop_token。
沒有關係。
jthread 會判斷這個函式能不能接收自動提供的 stop token。
可以簡單理解成:
如果函数可以这样调用:
worker(stop_token, args...)
→ 自动传入 stop_token
否则如果函数只能这样调用:
worker(args...)
→ 就像普通 thread 一样调用
所以兩種寫法都可以:
void worker(std::stop_token token);
以及:
void worker();
3.2.帶額外引數的 stop_token 執行緒函式
例如:
#include <print>
#include <stop_token>
#include <thread>
void worker(std::stop_token token, int id)
{
std::println("worker {}", id);
while (!token.stop_requested())
{
// work
}
}
int main()
{
std::jthread thread(worker, 7);
thread.request_stop();
}
這裡:
std::jthread thread(worker, 7);
可以理解成最終呼叫:
worker(自动提供的 stop_token, 7);
所以如果函式需要 stop token,引數順序通常是:
void worker(std::stop_token token, 参数1, 参数2, ...)
例如:
void worker(
std::stop_token token,
int id,
std::string name);
建立:
std::jthread thread(worker, 7, "camera");
相當於:
worker(
自动提供的 stop_token,
7,
"camera"
)
3.3.jthread 仍然可以手動 join()
雖然 jthread 會自動 join,但你仍然可以自己寫:
std::jthread thread(work);
thread.join();
執行完:
thread.join();
以後:
thread.joinable()
會變成:
false
因為這個 jthread 已經不再關聯一個可 join 的執行執行緒。
所以:
自动 join
並不代表:
不能手动 join
它只是意味著:
如果你沒手動處理,解構函式會幫你安全收尾。
3.4.jthread 也可以 detach(),但通常不推薦
jthread 仍然提供:
thread.detach();
但這樣做以後:
jthread 不再拥有这个线程
析构时也没办法再 join 它
於是 jthread 最重要的 RAII 生命週期優勢基本就沒有了。
所以如果你發現自己準備寫:
thread.detach();
最好先問幾個問題:
这个线程以后由谁负责?
程序退出时它怎么结束?
它访问的对象会不会已经析构?
它访问的资源能活多久?
除非你非常明確地知道執行緒生命週期怎麼管理,否則一般不建議隨便 detach。
3.5.condition_variable_any 與 stop_token
前面提到一個問題:
如果執行緒正在:
condition_variable.wait(...)
裡面睡眠,那麼只檢查:
token.stop_requested()
是不夠的。
因為執行緒可能根本沒機會執行到下一次檢查。
C++20 的:
std::condition_variable_any
提供了可以直接配合 stop_token 的等待方式。
例如:
#include <condition_variable>
#include <mutex>
#include <stop_token>
#include <thread>
std::mutex mutex;
std::condition_variable_any cv;
bool ready = false;
void worker(std::stop_token token)
{
std::unique_lock lock(mutex);
bool condition_met = cv.wait(
lock,
token,
[] {
return ready;
});
if (!condition_met)
{
// 没等到 ready == true,
// 而是因为停止请求结束等待
return;
}
// ready == true
}
可以把這次等待理解成:
等待两个条件中的任意一个:
1. ready == true
或者
2. 收到停止请求
如果:
condition_met == true
表示謂詞:
ready == true
成立。
如果:
condition_met == false
說明這次等待並不是因為 ready 成立而結束,通常就是因為收到了停止請求。
這樣工作執行緒即使正在條件變數裡等待,也能夠響應 jthread 的停止協議。
4.選擇與常見錯誤
4.1.std::thread 和 std::jthread 對比
| 特性 | std::thread |
std::jthread |
|---|---|---|
| 引入版本 | C++11 | C++20 |
| 建立執行緒 | ✅ | ✅ |
手動 join() |
✅ | ✅ |
手動 detach() |
✅ | ✅ |
| 析構時自動收尾 | ❌ | ✅ |
joinable 狀態析構會 terminate() |
✅ | ❌ |
| 自帶停止請求機制 | ❌ | ✅ |
自動提供 stop_token |
❌ | ✅ |
| 適合長期可停止 worker | 一般 | ✅ |
如果專案可以使用 C++20,對於這種執行緒:
启动
↓
持续运行
↓
程序退出时请求停止
↓
安全结束
通常優先考慮:
std::jthread
4.2.atomic<bool> running 和 stop_token 怎麼選
傳統程式碼經常這樣寫:
std::atomic<bool> running{true};
void worker()
{
while (running.load())
{
// work
}
}
停止時:
running.store(false);
這種寫法沒有問題。
對於非常簡單的執行緒,它甚至很直觀。
例如:
running == true
→ 继续运行
running == false
→ 停止
但是:
std::stop_token
提供的是一套標準化的停止協議。
它可以和:
std::jthread
std::stop_source
std::stop_callback
std::condition_variable_any
直接配合。
可以理解成:
atomic<bool>
→ 只是一个线程安全的 bool
→ “true / false 到底是什么意思”由你自己规定
而:
stop_token
→ 专门表达“停止请求”
→ 标准库里的多个工具都认识这种协议
因此:
非常简单的开关
→ atomic<bool> 完全可以
明确的线程停止生命周期
→ jthread + stop_token 更合适
4.3.常見錯誤
4.3.1.錯誤:以為 request_stop() 會強制殺死執行緒
錯誤理解:
request_stop()
→ 线程立刻消失
正確理解:
request_stop()
→ 发出停止请求
→ 工作线程自己检查并退出
4.3.2.錯誤:worker 從來不檢查 stop_requested()
例如:
void worker(std::stop_token)
{
while (true)
{
do_work();
}
}
即使 jthread 析構時自動:
request_stop();
這個執行緒也不會退出。
最終析構中的:
join();
會一直等。
4.3.3.錯誤:檢查了停止請求,但中間阻塞太久
例如:
while (!token.stop_requested())
{
blocking_operation(); // 一卡几十秒
}
停止請求雖然已經發出,但執行緒要等到阻塞操作返回後才能再次檢查。
4.3.4.錯誤:用了 jthread 又隨手 detach()
這樣會失去:
自动 join
自动停止请求
RAII 生命周期管理
這些主要優勢。
4.3.5.錯誤:在 stop_callback 裡做大量複雜工作
callback 更適合:
轻量通知
设置状态
唤醒等待者
而不是承擔複雜業務邏輯。
4.3.6.錯誤:以為“收到停止請求”就會立刻終止執行緒(重要)
request_stop() 只是發出停止請求,並不會強制中斷工作執行緒。
工作執行緒通常會在某些安全檢查點主動檢查:
token.stop_requested()
例如:
while (!token.stop_requested())
{
finish_one_small_task();
}
如果停止請求恰好在 finish_one_small_task() 執行過程中到來,這個函式不會被強行執行到一半就終止。
執行緒通常會先完成當前這一小塊工作,然後回到迴圈條件:
token.stop_requested()
發現停止請求後,再退出迴圈並結束執行緒。
可以理解成:
收到停止请求
↓
完成当前不能被随意打断的小任务
↓
到达安全检查点
↓
发现 stop_requested() == true
↓
退出循环
↓
完成必要的收尾
↓
线程结束
而 join() 的作用只是:
等待這個執行緒真正結束。
所以典型關係是:
request_stop()
→ 请求线程停止
worker 在安全位置响应请求并退出
join()
→ 等待 worker 退出完成
所謂“合適、安全的位置”,就是執行緒狀態已經完整、資源處於一致狀態,此時退出不會留下半完成操作的位置。
安全點 = 可以開始退出的位置;函式 return = 執行緒真正退出。
所以可以這麼理解例子中的worker函式 返回 ≈ 這個執行緒執行結束。
5.小結
std::jthread 可以理解成:
帶 RAII 生命週期管理和標準停止協議的
std::thread。
最重要的幾個點:
std::thread
→ 必须自己保证 join() / detach()
std::jthread
→ 析构时自动 request_stop() + join()
停止機制:
request_stop()
→ 发出停止请求
stop_token.stop_requested()
→ 查看是否收到停止请求
它是:
协作式停止
而不是:
强制杀死线程
另外:
stop_source
→ 发出停止请求
stop_token
→ 观察停止请求
stop_callback
→ 停止请求出现时执行回调
對於 C++20 中“啟動後持續執行、退出時需要安全停止”的後臺執行緒,一般可以優先考慮:
std::jthread + std::stop_token
而不是:
std::thread + 手写生命周期管理 + 自定义 atomic<bool> 停止协议