模式定义
保证一个类只能生成一个实例对象。
单线程版本
1 class Singleton{ 2 private: 3 // 私有化构造函数和拷贝构造函数 4 Singleton(); 5 Singleton(const Singleton& other); 6 public: 7 // 静态对象指针和静态获取对象指针函数 8 static Singleton* getInstance(); 9 static Singleton* m_instance; 10 }; 11 // 初始化 12 Singleton* Singleton::m_instance=nullptr; 13 14 //线程非安全版本 15 Singleton* Singleton::getInstance() { 16 if (m_instance == nullptr) { 17 m_instance = new Singleton(); 18 } 19 return m_instance; 20 } 21 22 //线程安全版本,但锁的代价过高 23 Singleton* Singleton::getInstance() { 24 Lock lock; 25 if (m_instance == nullptr) { 26 m_instance = new Singleton(); 27 } 28 return m_instance; 29 }
多线程版本
class Singleton{ private: // 私有化构造函数和拷贝构造函数 Singleton(); Singleton(const Singleton& other); mutex mymutex; //互斥量 public: // 静态对象指针和静态获取对象指针函数 static Singleton* getInstance(); static Singleton* m_instance; }; // 初始化 Singleton* Singleton::m_instance=nullptr;
线程安全版本,但锁的代价过高
1 //线程安全版本,但锁的代价过高 2 Singleton* Singleton::getInstance() { 3 unique_lock<mutex> mylock(mymutex); 4 if (m_instance == nullptr) { 5 m_instance = new Singleton(); 6 } 7 return m_instance; 8 }
双检查锁版本(会出错)
1 //双检查锁 2 // 但由于内存读写reorder不安全, 编译器的优化有关 3 // 在汇编层面m_instance = new Singleton()语句可能的执行顺序是 4 // 先分配内存地址,再执行构造函数,再赋值给内存,导致第二次检查结果为false,但是这时对象还没创建 5 Singleton* Singleton::getInstance() { 6 // 这里的检查,在已经创建了单例对象后,就没有必要加锁 7 if(m_instance==nullptr){ 8 Lock lock; 9 // 在检查一次是为了避免两个线程都通过了第一个检查,都会创建的对象的情况 10 if (m_instance == nullptr) { 11 m_instance = new Singleton(); 12 } 13 } 14 return m_instance; 15 }
原子操作版本
1 //C++ 11版本之后的跨平台实现 (volatile) 2 // 或者使用原子操作 3 std::atomic<Singleton*> Singleton::m_instance; 4 std::mutex Singleton::m_mutex; 5 6 Singleton* Singleton::getInstance() { 7 Singleton* tmp = m_instance.load(std::memory_order_relaxed); 8 std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence 9 if (tmp == nullptr) { 10 std::lock_guard<std::mutex> lock(m_mutex); 11 tmp = m_instance.load(std::memory_order_relaxed); 12 if (tmp == nullptr) { 13 tmp = new Singleton; 14 std::atomic_thread_fence(std::memory_order_release);//释放内存fence 15 m_instance.store(tmp, std::memory_order_relaxed); 16 } 17 } 18 return tmp; 19 }
综上,在多线程中,推荐使用原子操作版。