muduo源码分析2——Singleton分析

2023-11-05

1. 一般singleton写法

单例模式即要求只有在第一次调用的时候创建该对象,主要分为以下两条路(返回指针还是引用),返回引用可以防止使用中delete instance导致对象被提前销毁:

  1. private包含static指针以及构造函数,public里创建函数,通过调用其构造函数,返回该指针
  2. private包含static指针以及构造函数,public里创建函数,通过调用其构造函数,返回该引用
  3. private包含构造函数,public里创建函数,该函数里设置static变量实例,返回其引用

实际写单例的时候,需要考虑两个问题

  1. 是否需要delete,如何delete
  2. 是否考虑竞争

如果使用一般的创建函数,new一个对象,则需要考虑如何delete,否则会出现内存泄漏,这里也可以使用智能指针,但是会相对麻烦,且更耗资源。本文暂不考虑。

懒汉模式(不考虑多线程,且不考虑内存泄漏,没有返回引用)

  class singleton   //实现单例模式的类  
  {  
  private:  
      singleton(){}  //私有的构造函数  
      static singleton* instance;  
  public:  
      static singleton* GetInstance()  
      {  
          if (instance== NULL) //判断是否第一调用  
              instance= new singleton();  
          return instance;  
      }  
  }; 

下面两个只调用了一次构造函数
singleton* instance_1 =  singleton::GetInstance();
singleton* instance_2 =  singleton::GetInstance();

懒汉模式(多线程),增加一层instance== NULL判断,以及Lock()

       if (instance== NULL) //判断是否第一调用  
       {   
           Lock(); //表示上锁的函数  
           if (instance== NULL)  
           {  
               instance= new singleton();  
           }  
           UnLock() //解锁函数  
       }   

2. muduo::singleton

muduo里singleton主要做了以下动作

  1. 返回&
  2. 使用atexit,在main函数返回或者exit时,完成delete调用,完成栈清理
  3. 使用pthread_once设置PTHREAD_ONCE_INIT,只在第一次调用new,避免多线程竞争,多次new

其暴露了instance函数,因为其返回引用,可以对T实例对象修改,但保证只有一个对象

2.1 singleton代码

template<typename T>
class Singleton : noncopyable
{
 public:
  Singleton() = delete;
  ~Singleton() = delete;

  static T& instance()
  {
    pthread_once(&ponce_, &Singleton::init);
    assert(value_ != NULL);
    return *value_;
  }

 private:
  static void init()
  {
    value_ = new T();
    if (!detail::has_no_destroy<T>::value)
    {
      ::atexit(destroy);
    }
  }

  static void destroy()
  {
    typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
    T_must_be_complete_type dummy; (void) dummy;

    delete value_;
    value_ = NULL;
  }

 private:
  static pthread_once_t ponce_;
  static T*             value_;
};

template<typename T>
pthread_once_t Singleton<T>::ponce_ = PTHREAD_ONCE_INIT;

template<typename T>
T* Singleton<T>::value_ = NULL;

2.2 muduo 测试代码

#include "muduo/base/Singleton.h"
#include "muduo/base/CurrentThread.h"
#include "muduo/base/Thread.h"

#include <stdio.h>

class Test : muduo::noncopyable
{
 public:
  Test()
  {
    printf("tid=%d, constructing %p\n", muduo::CurrentThread::tid(), this);
  }

  ~Test()
  {
    printf("tid=%d, destructing %p %s\n", muduo::CurrentThread::tid(), this, name_.c_str());
  }

  const muduo::string& name() const { return name_; }
  void setName(const muduo::string& n) { name_ = n; }

 private:
  muduo::string name_;
};

class TestNoDestroy : muduo::noncopyable
{
 public:
  // Tag member for Singleton<T>
  void no_destroy();

  TestNoDestroy()
  {
    printf("tid=%d, constructing TestNoDestroy %p\n", muduo::CurrentThread::tid(), this);
  }

  ~TestNoDestroy()
  {
    printf("tid=%d, destructing TestNoDestroy %p\n", muduo::CurrentThread::tid(), this);
  }
};

void threadFunc()
{
  printf("tid=%d, %p name=%s\n",
         muduo::CurrentThread::tid(),
         &muduo::Singleton<Test>::instance(),
         muduo::Singleton<Test>::instance().name().c_str());
  muduo::Singleton<Test>::instance().setName("only one, changed");
}

int main()
{
  muduo::Singleton<Test>::instance().setName("only one");
  muduo::Thread t1(threadFunc);
  t1.start();
  t1.join();
  printf("tid=%d, %p name=%s\n",
         muduo::CurrentThread::tid(),
         &muduo::Singleton<Test>::instance(),
         muduo::Singleton<Test>::instance().name().c_str());
  muduo::Singleton<TestNoDestroy>::instance();
  printf("with valgrind, you should see %zd-byte memory leak.\n", sizeof(TestNoDestroy));
}

2.3 ThreadLocalSingleton

分析其代码:
muduo里ThreadLocalSingleton有如下特点

  1. 使用!t_value_判断是否null
  2. 在pthread_key_create里,注册destructor,线程结束时调用,完成栈清理
  3. 返回&
  4. 保证只有一个ThreadLocal对象(否则在主线程中可以创建多个TSD对象,即多个key指针)
namespace muduo
{

template<typename T>
class ThreadLocalSingleton : noncopyable
{
 public:
  ThreadLocalSingleton() = delete;
  ~ThreadLocalSingleton() = delete;

  static T& instance()
  {
    if (!t_value_)
    {
      t_value_ = new T();
      deleter_.set(t_value_);
    }
    return *t_value_;
  }

  static T* pointer()
  {
    return t_value_;
  }

 private:
  static void destructor(void* obj)
  {
    assert(obj == t_value_);
    typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
    T_must_be_complete_type dummy; (void) dummy;
    delete t_value_;
    t_value_ = 0;
  }

  class Deleter
  {
   public:
    Deleter()
    {
      pthread_key_create(&pkey_, &ThreadLocalSingleton::destructor);
    }

    ~Deleter()
    {
      pthread_key_delete(pkey_);
    }

    void set(T* newObj)
    {
      assert(pthread_getspecific(pkey_) == NULL);
      pthread_setspecific(pkey_, newObj);
    }

    pthread_key_t pkey_;
  };

  static __thread T* t_value_;
  static Deleter deleter_;
};

template<typename T>
__thread T* ThreadLocalSingleton<T>::t_value_ = 0;

template<typename T>
typename ThreadLocalSingleton<T>::Deleter ThreadLocalSingleton<T>::deleter_;

}  // namespace muduo

2.2 pthread_once

pthread_once()调用会出现在多个线程中,init_routine()函数仅执行一次,究竟在哪个线程中执行是不定的,是由内核调度来决定

int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));

使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次

2.3 atexit的使用

在这里插入图片描述
值得注意的点在于atexit函数的调用,其在exit(3)或者main函数结束的时候,都会被调用,回顾下进程正常终止的五个情况,main函数返回等同于exit(3),调用多次相当于进行函数的压栈操作,先入后出。
在这里插入图片描述

文章链接
C++ 单例模式总结与剖析

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

muduo源码分析2——Singleton分析 的相关文章

  • C++基础(10)——函数模板和类模板

    前言 本文主要介绍了C 中函数模板和类模板基本知识 6 1 函数模板 模板函数的定义 template
  • 智能指针与句柄详解(一)

    前言 智能指针与引用计数详解 一 中提到实现智能指针有两种方法 一种是引用计数 另一种就是句柄类实现 什么是句柄类 句柄类是用来存储和管理基类指针 指针所指对象的类型可以变化 它既可以指向基类类型对象又可以指向派生类型对象 用户通过句柄类访
  • 关于C++中constexpr的用法

    在C 11 primer中 关于constexpr用法给出的解释是 允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式 声明为constexpr的变量一定是一个常量 而且必须用常量表达式初始化 第一句中 c
  • C++类中this指针的理解

    https www cnblogs com liushui sky p 5802981 html 很好的文
  • C++——并发之std::async使用注意的坑,以及promise,future

    promise背景1 promise背景2 async 1 async 与promise future 1 1 async以及获取时间 async函数如下所示 template
  • 分数运算(类+构造)

    题目描述 用C 定义和实现一个分数类 并根据要求完成分数对象的运用 分数类包含分子和分母两个属性 操作包括 各属性的get方法 构造函数 初始化分子分母 相加运算 该运算接收两个分数对象的分子和分母 然后进行分数相加 结果保存在自己的分子和
  • C++之对封装、继承、多态的理解

    目录 一 对封装 继承和多态的简单理解 二 举例 1 封装的例子 2 继承的例子 3 多态的例子 三 代码实现 1 封装 C 或Java实现 2 继承 C 或Java实现 3 多态 C 或Java实现 四 以一个简单的实例 剖析 封装 的实
  • c++学习——构造函数和析构函数

    构造函数和析构函数 简要概述 构造函数和析构函数的简单调用 构造函数和析构函数能够函数重载 默认的构造函数和析构函数 拷贝构造 构造函数的分类和调用 匿名对象 拷贝构造函数的调用时机 构造函数的调用规则 多个对象的构造函数和析构函数 深浅拷
  • osgEarth的Rex引擎原理分析(一二三)osgEarth的缓存及其结构

    目标 十七 中问题43 1 缓存分两类 1 文件缓存 osgDB FileCache FileSystemCache 位于osgEarthDrivers cache filesystem FileSystemCache osgDB File
  • c++ char数组转string

    代码 char数组的路径转string的路径 void charArrayPath2string char char array path MAX PATH std string string path std stringstream s
  • STL——vector以及emplace_back分析

    1 这里需要注意凡是连续空间的容器都提供operator 是为了数组操作 2 back 应该是 end 1 3 vector的大小为12 vector的迭代器为指针 1 emplace back 1 相比push back 如果传入临时对象
  • Cpp关键字破解(三)【volatile】篇

    关键字总结 volatile 文章目录 关键字总结 volatile 0 前言 1 概念 2 作用 3 使用场景 4 volatile成员函数 5 代码体验 0 前言 参考几位前辈博客 汇总整理了一下 C 中volatile关键字的使用详解
  • C#各种结束进程的方法详细介绍

    转自http www cnblogs com zjoch p 3654940 html Process类的CloseMainWindow Kill Close Process CloseMainWindow是GUI程序的最友好结束方式 从名
  • C++学习(四八七)android studio println的输出位置

    程序中调用如下输出 System out println haha1 调试情况下 在Run和LogCat下均看不到输出 运行情况下 在Run下能看到输出 建议如下 可在LogCat中看到信息 android util Log常用的方法有以下
  • c++编写COM组件,并使用该组件

    在网上看了很多个介绍com组件的方法 对于一个新手来说看很久都看不懂 自己项目需要实现com 于是自己整理了一个文档和代码 先记录下来 以防以后用的上 步骤如下 1 新建ATL项目 你也可以是其他项目 只要是dll就行 可以支持MFC AT
  • C++征途 --- List链表容器

    第一部分 基础概念 上面这个模型的是一个单向链表 优点 1 链表增加和删除节点的时候不需要进行vector数组那样的增完后进行后移 也不需要删完后前移 当它增加一个节点的时候 只需要将它插入的位置的上一个节点的指针域中的指针指向增加的节点
  • STL之set常见用法详解

    摘自胡凡的 算法笔记 仅作记录用 前言 set是一个内部自动有序且不含重复元素的容器 如果要使用set 需要添加set头文件 即 include
  • C++中的friend详细解析

    https blog csdn net u012861978 article details 52095607
  • vector模拟实现

    个人简介 作者简介 大家好 我是菀枯 支持我 点赞 收藏 留言 格言 不要在低谷沉沦自己 不要在高峰上放弃努力 1 前言 大家在学习C 的时候一定会学到STL 标准模板库 这是C 标准库中最重要的组成部分 它包含了常用的数据结构和算法 今天
  • acwing算法提高之动态规划--数字三角形模型

    目录 1 基础知识 2 模板 3 工程化 1 基础知识 暂无 2 模板 暂无 3 工程化 题目1 摘花生 解题思路 DP 状态定义 f i j 从 1 1 走到 i j 所摘花生总和 状态转移 有 从上方走到 i j 有 f i 1 j w

随机推荐

  • error: C2248: “QObject::QObject”: 无法访问 private 成员(在“QObject”类中声明)

    QT中使用的C 对象经常会用到数据类 而存放数据可以选择使用QList lt gt QMap lt gt 等模板类存放指针或是对象 如果是选择存数据对象 考虑好之后的数据最好是静态访问的 很少去修改的 在存放的时候就会报上面的错误 原因是没
  • 各种通信接口的简单对比

    对比表 同步方式与异步方式的主要区别在于 是否传输时钟信号 只要是通訊前雙方需要設定相同波特率的 都是異步傳輸方式 异步传输 Asynchronous Transmission 每次异步传输的信息都以一个起始位开头 它通知接收方数据已经到达
  • 微服务之间调用的异常应该如何处理

    前言 在分布式服务的场景下 业务服务都将进行拆分 不同服务之间都会相互调用 如何做好异常处理是比较关键的 可以让业务人员在页面使用系统报错后 很清楚的看到服务报错的原因 而不是返回代码级别的异常报错 比如NullException Ille
  • datetime 模块详解 -- 基本的日期和时间类型

    转自 https www cnblogs com fclbky articles 4098204 html datetime 模块提供了各种类用于操作日期和时间 该模块侧重于高效率的格式化输出在 Python 中 与时间处理有关的模块包括
  • 6-11 删除字符 (20 分)

    本题要求实现一个删除字符串中的指定字符的简单函数 函数接口定义 void delchar char str char c 其中char str是传入的字符串 c是待删除的字符 函数delchar的功能是将字符串str中出现的所有c字符删除
  • Nginx+FastCGI参数传递

    如果需要将需要将各种参数传递到fcgi 例如传递请求参数 请求方法等到fcgi 需要在nginx中加配置 location fcgi fastcgi pass ip port fastcgi param QUERY STRING query
  • 汉诺塔的相关应用

    汉诺塔的应用 是递归的一种比较例子 题目藐视见下面 就是一个递归的实现 先把a上的n 1个盘同过c移到b 再把a上的最后一只盘移到c 随后再把b上的n 1只盘通过a 移到c 描述就是这样 include
  • 两个接口和一个类的适配器模式

    适配器类实现其中一个接口方法 创建另一个接口的对象和构造方法 在接口方法中调用另一个接口的方法 实例如下 public class shipeiqi public static void main String args ATable aT
  • 基于神经网络的目标检测论文之DenseNet:密集连接的卷积神经网络

    第三章 基于密集连接卷积网络改进的目标分类算法 最近的研究表明 如果卷积网络包含接近输入的层和接近输出的层之间的较短连接 则卷积网络可以更深入 更精确和更有效地进行训练 在本章中 论文首先研究密集卷积网络 DenseNet 的结构和工作原理
  • USB转RS485串口电路设计

    USB转串口芯片的串口信号一般为 TTL CMOS电平 在实现半双工 RS485 串口时需要外接485电平转换芯片 设计中需要有信号来控制 485 转接芯片的发送和接收使能端 建议选择自带485控制引脚的转接芯片 如 CH340 CH342
  • MybatisPlus中removeById删除数据库未变

    removeById Serializable id 传入的是id Integer Long等 不是实体对象 就是对应你表的主键 由于我刚开始建表时未设置主键mybatisplus自动生成未在实体类表中标注主键 后加了主键 所以需在实体类主
  • 各种遥感数据,地理信息数据共享网站

    各种遥感数据 地理信息数据共享网站 至少一百 Online Global Satellite Image and Atlas http library gmu edu resources sci Geog579 htm 可以下载Aster
  • Java开发中的23种设计模式详解

    设计模式 Design Patterns 可复用面向对象软件的基础 设计模式 Design pattern 是一套被反复使用 多数人知晓的 经过分类编目的 代码设计经验的总结 使用设计模式是为了可重用代码 让代码更容易被他人理解 保证代码可
  • word怎么让封面、目录没有页码,页码从正文开始

    word怎么让封面 目录没有页码 页码从正文开始 1 开始插入页码 从第一页开始 如图 二 如果前两页是封面和目录 再从第一页开始就不合适了 解决步骤如下 1 在第三页的文字前面添加分页符 效果如图 2 选中第三页的页码 跳到设置页眉页脚的
  • Windows powershell增设快捷指令(Git版)

    1 创建并修改Windows Powershell 启动执行文件 echo PROFILE 输出的是powershell的执行文件路径 2 切换到WindowPowerShell路径下 创建文件 Microsoft PowerShell p
  • MySQL 数据类型

    目录 数据类型 数据类型分类 数值类型 bit类型 小数类型 float decimal 字符串类型 char varchar char和varchar比较 日期和时间类型 enum和set 集合查询使用find in set函数 afte
  • 【学习总结】EasyExcel合并同列不同行,表格数据相同的行

    实体类 Data HeadRowHeight 50 ContentStyle horizontalAlignment HorizontalAlignmentEnum CENTER verticalAlignment VerticalAlig
  • zookeeper 入门(一)单机zk

    topic 1 单机zk搭建 连接zk服务创建节点 以下简称zookeeper 为zk 我的操作环境 mac os jdk8 zookeeper 3 4 12 1 下载zk到本地 解压 tar zxvf zookeeper 3 4 12 t
  • [html]js无缝循环滚动图片示例代码

    html代码 div ul li a href YunNan html img src 微信图片 20200621003327 jpg 1 a li li a href Switzerland html img src 微信图片 20200
  • muduo源码分析2——Singleton分析

    1 一般singleton写法 单例模式即要求只有在第一次调用的时候创建该对象 主要分为以下两条路 返回指针还是引用 返回引用可以防止使用中delete instance导致对象被提前销毁 private包含static指针以及构造函数 p