已使用std::mutex保护访问时,是否仍需使用std::atomic?
问题解答
代码是否存在未定义行为?
这段代码没有未定义行为。核心原因是所有对sm_foo的读写操作都被同一个std::mutex完全保护:
- 每次调用
GetFoo()都会先获取锁,确保同一时间只有一个线程能进入临界区。 sm_foo的初始化(内存分配、成员变量赋值)全程在锁的保护下完成,其他线程只有在初始化彻底完成后,才能通过锁的同步看到完整的Foo对象。
注意:代码里存在一个语法错误——全局变量定义Foo* sm_foo = nullptr;应该改为Foo* Foo::sm_foo = nullptr;,否则会定义一个全局的sm_foo而非类的静态成员,但这属于语法问题,并非多线程相关的未定义行为。
使用std::mutex是否符合C++内存模型?
完全符合。C++内存模型明确规定,std::mutex的加锁(lock())和解锁(unlock())操作自带内存屏障语义:
- 解锁操作会强制将临界区内的所有内存写操作同步到主存,确保后续获取同一锁的线程能看到这些修改。
- 加锁操作会同步线程的内存状态,保证线程读取到的是最新的主存数据。
简单来说,锁的存在让临界区内的操作等价于按顺序执行,不会出现指令重排导致的对象未完全初始化就被读取的问题。
弱内存序平台上的表现
在ARM、PowerPC这类弱内存序平台上,这段代码依然能正常运行,不会出现问题。因为std::mutex的同步语义是C++标准强制要求的,与平台底层内存序无关:
- 锁的底层实现会针对对应平台插入必要的内存屏障指令,确保临界区内的写操作不会被重排到解锁之后,读操作不会被重排到加锁之前。
- 对比无锁的双重检查锁定模式(那种场景下弱内存序会导致对象未完全初始化就被读取),这段代码全程用锁保护,不存在此类风险。
内容的提问来源于stack exchange,提问作者Dyllon Gagnier
相关产品推荐
相关产品推荐

