单例模式、STL、智能指针下的线程安全问题

线程安全的单例模式

我们首先要知道什么是单例模式:单例模式是一种经典的、常用的设计模式。

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。

使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。

单例模式的特点

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

单例模式的分类

  • 懒汉模式:“延时加载,即用的时候再加载”,从而能够优化服务器的启动速度
  • 饿汉模式:就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。优点:简单。缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。

饿汉方式实现单例模式

  class Singleton 
  { 
   public: 
       static Singleton* GetInstance() 
       { 
           return &m_instance; 
       } 
   private: 
       //构造函数私有 
      Singleton() 
     {} 
      //C++11 防拷贝 
     Singleton(Singleton const&) = delete; 
     Singleton& operator = (Singleton const&) = delete; 
 }; 
 Singleton Singleton::m_instance;//在程序入口之前就完成单例对象的初始化

如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞
争,提高响应速度更好。

懒汉方式实现单例模式

  template <typename T>
  class Singleton
   {
       static T* inst;
    public:
       static T* GetInstance()
       {
           if(inst == NULL)
           {
              inst = new T();
          }
          return inst;
      }                                                                              
  };

从上面的代码可以看出一个严重的问题:线程不安全!

第一次调用GetInstance的时候,如果两个线程同时调用,可能会创建出两份T对象的实例。但是后续再次调用就没有问题了。

懒汉模式实现单例模式(线程安全版本)

线程安全版本

注意:

  • 加锁解锁的位置
  • 双重if判定,避免不必要的锁竞争
  • volatile关键字防止过度优化

STL、智能指针的线程安全问题

STL中的容器不是线程安全的。原因在于,STL的设计初衷是将性能挖掘到极致,而一旦涉及到加锁保证线程安全,就会对性能造成巨大的影响。而且对于不同的容器,加锁方式不同,性能可能也不同。因此STL默认不是线程安全的。如果需要在多线程环境下使用,需要调用者自行保证线程安全。

智能指针的线程安全问题:

对于unique_ptr,由于只是在当前代码块范围内生效,因此不涉及线程安全问题。对于share_ptr,多个对象需要共用一个引用计数变量,所以会存在线程安全问题。但标准库实现的时候考虑到了这个问题,基于原子操作的方式保证share_ptr能够实现原子的操作引用计数。

其他常见的各种锁

除了互斥锁以外还有其他锁,下面将一一介绍:

悲观锁:在每次取数据时,总是担心数据会被其他程序修改,所以会在取数据前先加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,被阻塞挂起。

乐观锁:每次取数据时候,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要采用两种方式:版本号机制和CAS操作。
CAS操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不断重试。

自旋锁:等待时间短,进行轮旋检测,即每隔一段时间检查临界资源是否还被占用。而常规的锁等待时间长。我们可以根据线程在临界资区中待的时间长短来判断是自旋锁还是其他锁。

发布了119 篇原创文章 · 获赞 17 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/tangya3158613488/article/details/103100372