std::atomic_thread_fence(std::memory_order_acquire) 是 C++11 标准中的一种内存栅栏(Memory Fence)操作。它用于在多线程程序中提供内存序的同步,确保特定的内存操作在内存模型中的顺序。
内存栅栏(Memory Fence)
内存栅栏是一种用于控制编译器和 CPU 对内存操作重排行为的工具。在多线程程序中,编译器和 CPU 可能会出于优化的目的对指令进行重排,而这种重排在单线程程序中是安全的,但在多线程程序中可能会导致数据不一致或竞态条件。
std::memory_order_acquire 的含义
std::memory_order_acquire 是 C++11 标准定义的一种内存顺序模型,表示在执行此操作之前的所有读取和写入操作都不会被重排到此操作之后。简而言之,使用 memory_order_acquire 的内存栅栏可以确保在此栅栏之后的操作不会被重排到栅栏之前。
使用场景
std::atomic_thread_fence(std::memory_order_acquire) 通常用于确保在读取共享资源之前,所有先前的写操作(通常是其他线程写入的)都已经完成,并且读取操作不会在内存模型中被重排到栅栏之前。这种机制可以在以下场景中使用:
- 线程间同步: 确保在读取某个共享变量之前,其他线程对这个变量的写操作已经完成。
- 锁的实现: 在自定义实现锁或其他同步原语时,使用 memory_order_acquire 的栅栏来确保在获取锁后看到的是最新的数据。
比如双重检查锁定中的使用,考虑双重检查锁定(Double-Checked Locking)模式,确保在初始化单例对象时多线程环境下的安全性。:
#include <atomic>
#include <mutex>
class Singleton {
public:
static Singleton* getInstance() {
Singleton* tmp = _instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(_mutex);
tmp = _instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton();
std::atomic_thread_fence(std::memory_order_release);
_instance.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}
private:
Singleton() = default;
~Singleton() = default;
static std::atomic<Singleton*> _instance;
static std::mutex _mutex;
};
// 静态成员变量定义
std::atomic<Singleton*> Singleton::_instance(nullptr);
std::mutex Singleton::_mutex;
工作流程
- 初始读取: 首先读取 _instance,使用 std::memory_order_relaxed,这是因为我们不关心顺序,只需要获取当前值。
- 内存栅栏(Acquire): 在读取 _instance 后,插入一个 std::atomic_thread_fence(std::memory_order_acquire)。这个内存栅栏确保任何在这个栅栏之前的读取操作不会在栅栏之后发生,即后续操作可以看到所有在其他线程中完成的写入。
- 检查是否需要初始化: 如果 _instance 为空,表示对象尚未初始化,进入锁定区。
- 初始化对象: 一旦对象被初始化后,使用 std::atomic_thread_fence(std::memory_order_release) 来插入一个内存栅栏,这确保初始化过程中的所有写操作在 _instance.store 之前完成。
- 存储结果: 使用 std::memory_order_relaxed 来存储结果,因为我们已经使用了 memory_order_release 来确保顺序性。
为什么使用内存栅栏
使用 std::atomic_thread_fence 可以确保在多线程环境下操作的顺序性。std::memory_order_acquire 的栅栏确保在对象初始化之后的读取操作能够看到最新的结果,而 std::memory_order_release 的栅栏则确保在写入操作之前,所有的初始化都已完成。
总结
std::atomic_thread_fence(std::memory_order_acquire) 是一种用于控制内存操作顺序的工具,确保在多线程程序中,特定的读取操作在内存模型中的顺序是正确的。它通常与 memory_order_release 搭配使用,以确保在读写共享资源时,线程能够正确地观察到其他线程的写入操作,从而避免竞态条件和数据不一致问题。