必须用 std::jthread 启动线程,因其自动管理 std::stop_source 生命周期并安全提供可拷贝的 std::stop_token;std::thread 不支持协作式停止,手动传 token 会因 stop_source 提前析构而失效,且析构不 join 可能触发 terminate。

必须用 std::jthread 启动线程,不能用 std::thread;std::stop_token 本身不中断任何东西,它只提供“被请求停止”的状态查询和阻塞点的协作唤醒能力。
为什么直接传 std::stop_token 给 std::thread 会失效
因为 std::thread 不管理 std::stop_source 生命周期:你手动构造的 std::stop_source 可能早于线程函数返回就析构了,导致 stoken.stop_requested() 永远返回 false;更糟的是,std::thread 析构时不自动 join(),容易触发 std::terminate。
正确做法只有这一种:
-
std::jthread构造时自动创建并绑定std::stop_source,其get_stop_token()返回的 token 才是安全可拷贝的 - 线程函数参数必须是
std::stop_token(或const std::stop_token&),否则无法接收 token - 不要自己
new std::stop_source或跨线程共享同一个实例——除非你明确需要多路触发
std::this_thread::sleep_for 和 cv.wait 怎么真正响应 stop_token
不是所有重载都支持 stop_token。必须用 C++20 新增的带 token 参数的版本,否则调用仍会无视停止请求。
立即学习“C++免费学习笔记(深入)”;
常见错误是写了 std::this_thread::sleep_for(100ms) 却没传 stoken,结果线程卡在 sleep 里直到超时才检查标志。
-
std::this_thread::sleep_for(100ms, stoken):被中断时抛std::stop_token::stop_requested异常(需try/catch) -
cv.wait(lock, stoken, [&]{ return !queue.empty(); }):第三个参数是谓词,会在被唤醒后自动检查stoken并返回false表示应退出 - 别用无参
cv.wait(lock)或旧版wait_for——它们对stop_token完全无感
遇到不支持 stop_token 的系统调用(如 read()、recv())怎么办
std::stop_token 是纯协作机制,它不会向内核发信号、也不会修改文件描述符状态。对原生系统调用,标准库不提供通用可取消封装。
此时只能靠“轮询 + 超时 + 显式检查”模拟协作:
- Linux 上用
poll()或epoll_wait(),把 socket 和一个 eventfd(由std::stop_callback关联到stop_source)一起等待 - Windows 上用
WSAEventSelect()+WaitForMultipleObjects(),将 stop 事件句柄加入等待列表 - 最通用但低效的方式:设短超时(如 10ms),每次
recv()返回EAGAIN后立即调用stoken.stop_requested()
切记:不要试图用 pthread_cancel 或封装 TerminateThread——C++20 明确放弃异步取消语义,这类操作必然导致资源泄漏或未定义行为。
析构时自动 request_stop() 不等于线程已退出
std::jthread 析构时确实会调用 request_stop() 并尝试 join(),但前提是线程还处于 joinable() 状态。如果线程函数已提前返回,join() 会立刻成功;但如果线程还在循环中处理长任务(比如大文件读写未分块),主线程可能已销毁 jthread 对象,而工作线程仍在运行。
这意味着:
- 别在
jthread析构后立刻访问它所独占的资源(如std::shared_ptr指向的缓冲区) - 关键清理逻辑(如
cleanup())必须放在线程函数末尾,且确保所有退出路径(包括异常)都能抵达 - 若需强同步,显式调用
t.join(),而不是依赖析构行为
最易被忽略的一点:std::stop_token 的有效性严格绑定于其所属 std::stop_source 的生命周期——一旦 std::jthread 析构完成,token 就变成 dangling,再调用 stop_requested() 是未定义行为。


















