当前位置: 首页 > news >正文

哈尔滨建设银行网站首页深圳网络推广服务公司

哈尔滨建设银行网站首页,深圳网络推广服务公司,做电商一般注册什么公司,行政审批网站开发文档目录 饿汉式懒汉式双检查锁,线程安全的版本什么是reorder?解决内存读写reorder不安全方法代码解释懒汉式的优缺点 单例模式是一种设计模式,用于确保一个类只有一个实例,并提供一个全局的访问点来获取该实例。它常用于需要在整个应…

目录

    • 饿汉式
    • 懒汉式
    • 双检查锁,线程安全的版本
    • 什么是reorder?
    • 解决内存读写reorder不安全方法
    • 代码解释
    • 懒汉式的优缺点

单例模式是一种设计模式,用于确保一个类只有一个实例,并提供一个全局的访问点来获取该实例。它常用于需要在整个应用程序中共享相同资源或状态的情况下。

单例模式分为饿汉式懒汉式

饿汉式

在饿汉式中,实例在类加载时就被初始化,并且保证在多线程环境下的线程安全。

// 饿汉式
class Singleton {
private:static Singleton* instance; // 静态成员变量,保存实例指针Singleton() {} // 构造函数私有化Singleton(const Singleton& other) {}	// 拷贝构造函数私有化Singleton& operator=(const Singleton&) {} // 赋值运算符私有化public:static Singleton* getInstance() {return instance;}
};Singleton* Singleton::instance = new Singleton(); // 在类加载时初始化实例// 使用示例
Singleton* obj1 = Singleton::getInstance();
Singleton* obj2 = Singleton::getInstance();// obj1 和 obj2 是同一个实例

饿汉式下类的实例对象在类加载时就被创建并赋值给静态成员变量instance,因此不需要考虑线程安全问题。因为每次调用getInstance方法都会返回同一个实例。

饿汉式的优点是实现简单,线程安全;缺点是无法实现延迟加载,即类在加载时就创建好了实例,可能会浪费资源。

懒汉式

懒汉式的实现方法是将实例的创建延迟到第一次请求访问时再进行初始化,这样可以避免初始化时资源的浪费和额外的开销,但需要考虑多线程之间的线程安全问题。

class Singleton
{
private:Singleton() {}Singleton(const Singleton& other) {}Singleton& operator=(const Singleton&) {} // 赋值运算符私有化
public:static Singleton* getInstance();static Singleton* Singleton::m_instance;
};Singleton* Singleton::m_instance = nullptr;// 线程不安全
Singleton* Singleton::getInstance()
{if (m_instance == nullptr)m_instance = new Singleton();return m_instance;
}

在getInstance方法中,我们先判断m_instance是否为空,为空就new一个出来。但是这样在单线程下是安全的,因为m_instance只会被创建一次,在多线程下可能会被创建多次。

双检查锁,线程安全的版本

class Singleton
{
private:Singleton() {}Singleton(const Singleton& other) {}Singleton& operator=(const Singleton&) {} // 赋值运算符私有化public:static Singleton* getInstance();static Singleton* m_instance;static std::mutex m_mutex;
};Singleton* Singleton::m_instance = nullptr;
std::mutex m_mutex;// 多线程安全,但锁的代价过高
Singleton* Singleton::getInstance()
{std::lock_guard<std::mutex> lock(m_mutex);if (m_instance == nullptr)m_instance = new Singleton();return m_instance;
}

这个版本在多线程下是安全的,因为加锁了,但是在读操作的情况下,也就是如果m_instance直接返回时是不需要加锁的,所以这个版本在高并发的情况下开销很大,很耗时,因为不管是写操作还是读操作都需要加锁减锁。

为了解决这个问题,我们可以使用双检查锁来避免这样的开销问题

// 双检查锁,但由于内存读写reorder不安全
Singleton* Singleton::getInstance()
{if (m_instance == nullptr){std::lock_guard<std::mutex> lock(m_mutex);if (m_instance == nullptr)m_instance = new Singleton();}return m_instance;
}

锁前检查是否为空是为了避免读操作下还进行加锁,锁后检查是为了避免多次创建。

但是这样还是有问题,内存读写reorder不安全。

什么是reorder?

reorder就是在编译器底层进行优化重排指令的执行顺序。

举个例子:
m_instance = new Singleton();
这行代码在编译器底层大致可以分为三个步骤:
1、分配内存
2、调用构造器对内存进行初始化
3、将内存的地址赋值给m_instance

但在实际的运行过程中,编译器执行的顺序可能是1-》3-》2,这就会导致当多个线程同时调用getInstance方法并且m_instance为nullptr时,它们可能会同时通过if语句的判断条件进入临界区。在这种情况下,第一个线程通过了if语句的条件检查,并在锁内部实例化了Singleton对象。但由于内存读写重排序的存在,编译器或处理器可能会将Singleton对象的初始化操作重排到锁的外部,这会导致第二个线程在第一个线程完成实例化之前通过了if语句的条件检查,直接返回使用,但此时m_instance还没有进行初始化。

解决内存读写reorder不安全方法

为了解决这个问题,我们可以使用如下代码实现,支持C11以上版本并跨平台

class Singleton
{
private:Singleton();Singleton(const Singleton& other);public:static Singleton* getInstance();static std::atomic<Singleton*> m_instance;static std::mutex m_mutex;
};std::atomic<Singleton*> Singleton::m_instance;
std::mutex m_mutex;Singleton* Singleton::getInstance()
{Singleton* tmp = m_instance.load(std::memory_order_relaxed);std::atomic_thread_fence(std::memory_order_acquire);if (tmp == nullptr){std::lock_guard<std::mutex> lock(m_mutex);tmp = m_instance.load(std::memory_order_relaxed);if (tmp == nullptr){tmp = new Singleton;std::atomic_thread_fence(std::memory_order_release);m_instance.store(tmp, std::memory_order_relaxed);}}return tmp;
}

代码解释

首先,使用了双重检查锁定来提高性能。代码开始时,通过调用 m_instance.load(std::memory_order_relaxed) 加载 m_instance 变量的值,并将结果赋给 tmp 变量。

接下来,通过调用 std::atomic_thread_fence(std::memory_order_acquire) 来添加内存屏障,保证之前的读操作完成后,之后的读写操作不会被重排序。

然后,通过判断 tmp 是否为 nullptr,来确定是否需要创建实例。如果 tmp 是 nullptr,表示还没有创建实例,需要进行创建。

在创建实例之前,先获取一个互斥锁 m_mutex,确保只有一个线程可以访问临界区代码。

再次检查 tmp 是否为 nullptr,是为了防止多个线程同时通过第一个检查而进入临界区,因为在第一个检查后可能已经有其他线程创建了实例。

在临界区内部,首先创造了一个 Singleton 类的实例 tmp。然后通过 std::atomic_thread_fence(std::memory_order_release) 添加内存屏障,确保在 tmp 赋值完成后,该实例的构造函数中的其他写操作不会被重排序。

最后,通过调用 m_instance.store(tmp, std::memory_order_relaxed) 将 tmp 存储到 m_instance 变量中。

在临界区外部,返回已经创建的实例 tmp。

这种实现方式既兼顾了性能又保证了线程安全。通过使用双重检查锁定和互斥锁,可以避免多个线程同时创建实例,同时使用原子操作和内存屏障来保证实例的可见性和有序性。

懒汉式的优缺点

优点:
1、延迟加载:懒汉式在需要用到实例的时候才创建,可以在程序启动时减少不必要的消耗。
2、节约内存:懒汉式只会在用到对象时创建,避免了无谓的内存占用。

缺点:
1、线程安全性问题:多线程下同时获取实例时,可能会造成实例被多次创建的问题
2、性能问题:在多线程环境下,为了保证实例被唯一创建,需要引入额外的同步开销,高并发下会影响性能
3、实现复杂:为了保证线程安全,需要使用双检查锁等方法增加了代码的复杂性。

http://www.hkea.cn/news/403062/

相关文章:

  • wordpress+模版+推荐专业网站seo推广
  • 浦项建设公司员工网站2023免费推广入口
  • 如何查询某个网站的设计公司最新推广注册app拿佣金
  • 八宝山做网站公司打广告
  • wordpress vip查看插件南宁seo费用服务
  • 建站之星模板怎么设置手机如何做网站
  • 上海公司网站制作价格西安百度关键词排名服务
  • 长沙网页制作开发公司aso优化方案
  • 深圳罗湖网站制作成人电脑基础培训班
  • 无锡网站制作咨询深圳网站设计十年乐云seo
  • 大连城市建设网站seo优化顾问服务阿亮
  • 福州 网站建设沈阳seo关键词排名优化软件
  • 做网站还要买服务器吗镇江seo
  • 专门做特价的网站优化排名案例
  • 网站建设的一些问题友链交易交易平台
  • 创业初期要建立公司的网站吗seo排名优化代理
  • 做网站全屏尺寸是多少钱站长工具查询系统
  • 做企业平台的网站有哪些手机网站制作教程
  • 免费行情的软件大全下载北京公司排名seo
  • 网站联系方式要素qq群推广链接
  • div css 网站模板免费的云服务器有哪些
  • 35互联做网站好吗网店运营工作内容
  • 网站建设模拟软件营销培训课程内容
  • 深圳建网站兴田德润专业2023年最新新闻简短摘抄
  • 学校网站怎么查询录取百度相册登录入口
  • 自助建设彩票网站网址查询工具
  • 怎么创建网页的快捷方式seo入门版
  • 互联网企业网站网络优化
  • 山东手工活外发加工网四川二级站seo整站优化排名
  • 行业门户网站开发百度竞价怎么做效果好