C++20中release序原子store后调用notify_all是否线程安全?
结论
你的写法完全可行,是线程安全的,你关于「store在执行序列上先于notify_all、二者不会被重排序」的判断符合C++标准要求。
示例代码
唤醒侧实现:
std::atomic_bool is_ready{}; void SetReady() { is_ready.store(true, std::memory_order_release); is_ready.notify_all(); }
等待侧实现:
void Wait() { is_ready.wait(false, std::memory_order_acquire); }
技术细节说明
- 关于重排序的约束
你提到的「release内存序无法保证编译器不会对其后的操作重排序」这个描述本身是成立的,但这个规则不适用于当前场景:- 同一个原子对象上的所有操作(包括
store、load、wait、notify_one/notify_all),在同一个线程内的程序序是被严格保证的,编译器和CPU都不允许将notify_all重排到同一个原子对象上位于它前面的store操作之前。如果允许这种重排,原子wait/notify机制本身就会出现不可避免的唤醒丢失问题,和标准库的设计目标相悖。 - 退一步说,哪怕存在理论上的重排可能,这个场景下也不会引发错误:
notify_all本身不读写任何业务共享数据,它的唯一作用是唤醒阻塞在该原子对象等待队列上的线程,不存在需要被同步的副作用。
- 同一个原子对象上的所有操作(包括
- 关于内存同步的正确性
这套实现的可见性保证是由store(release)和wait内部的load(acquire)配对提供的:- 等待线程调用
wait(false, std::memory_order_acquire)时,会先原子性加载is_ready的当前值,如果发现值已经不是false,会直接返回,不会进入阻塞;如果值是false才会进入阻塞等待通知。 - 阻塞中的线程被通知唤醒后,会重新执行上述原子加载检查流程,这次加载操作会和唤醒侧的
store(release)形成标准的release-acquire同步关系:唤醒侧在store之前的所有内存写操作,都会对wait返回后的等待线程完全可见。
- 等待线程调用
- 关于唤醒丢失的问题
这套实现不存在唤醒丢失风险:如果notify_all执行时,等待线程还没进入阻塞状态,那么等待线程后续执行wait时的第一次加载就会读到is_ready == true,直接返回,完全不会进入阻塞,自然不会错过通知。
补充说明
notify_one/notify_all本身不承担内存同步职责,这也是C++标准没有给这类通知操作设计内存序参数的原因——它只负责唤醒等待线程,内存可见性由配对的原子store、load操作的内存序保证。你当前选择的memory_order_release+memory_order_acquire配对是兼顾性能和正确性的最优选择,不需要额外增加内存屏障。
内容的提问来源于stack exchange,提问作者Denis319199
相关产品推荐
相关产品推荐

