使用stop_token等待condition_variable_any时,请求停止是否需持有锁?
在等待条件变量时,修改谓词状态的线程必须持有锁,避免唤醒期间错过更新——哪怕用原子变量,标准文档也要求这么做。但我不确定request_stop()是否需要遵循这个规则,想知道以下两种调用方式哪种符合标准规范?
另外,~jthread()调用request_stop()时自然不会持有锁,但我搞不懂stop_token和原子共享变量的区别:为什么前者调用request_stop()不需要锁,后者修改时却必须持有?
代码示例
#include <thread> #include <condition_variable> #include <iostream> #include <chrono> std::mutex m; std::condition_variable_any cv; void waitingThread(std::stop_token st){ std::unique_lock<std::mutex> lk(m); std::cout<<"Waiting"<<std::endl; cv.wait(lk, st, [](){return false;}); std::cout<<"Awake"<<std::endl; } void withoutLock(){ std::jthread jt{waitingThread}; std::this_thread::sleep_for(std::chrono::seconds(1)); jt.request_stop(); } void withLock(){ std::jthread jt{waitingThread}; std::this_thread::sleep_for(std::chrono::seconds(1)); { std::lock_guard<std::mutex> lk(m); jt.request_stop(); } } int main(){ withoutLock(); withLock(); }
相关标准文档说明
std::condition_variable的文档指出:即使共享变量是原子的,也必须在持有互斥锁的情况下修改它,才能将修改正确发布给等待线程。
而std::condition_variable_any::wait、std::stop_token和std::stop_source的文档未明确说明等待中断是否能保证记录停止状态的变更。
解答
哪种方式符合标准规范?
两种方式都符合标准规范,核心原因是condition_variable_any::wait与stop_token的集成实现,已经内部处理了同步问题,不需要额外手动加锁保护request_stop()的调用。
为什么stop_token不需要锁,而原子变量需要?
stop_token的内部同步机制:
stop_token和stop_source是C++20专为协作式停止设计的组件,它们的内部已经保证了停止状态的原子性修改与跨线程可见性。调用request_stop()时,不仅会原子性设置停止标记,还会自动触发所有关联等待操作(比如cv.wait(lk, st, ...))的唤醒——这个过程和条件变量的等待逻辑深度绑定,无需外部锁介入同步。原子变量的竞态风险:当自定义原子变量作为谓词一部分时,持锁修改是为了避免丢失唤醒的竞态:比如等待线程刚检查完谓词为false、释放锁准备休眠的间隙,修改线程修改了原子变量但还没调用
notify_one(),此时等待线程会错过唤醒陷入永久休眠。而stop_token与condition_variable_any::wait的组合已经消除了这个风险:等待逻辑在检查停止状态和进入休眠的过程中,内部保证不会错过request_stop()的通知。jthread析构的合理性:
~jthread()调用request_stop()无需锁,正是因为stop_token的内部同步机制已经足够安全,不需要依赖外部互斥锁来保证状态发布的正确性。
内容的提问来源于stack exchange,提问作者CGlas

