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++ 单例模式总结与剖析

muduo源码分析2——Singleton分析相关推荐

  1. Colly源码解析——结合例子分析底层实现

    通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...

  2. muduo源码剖析——Singleton单例模式之懒汉模式与DCL双重检查

    0 懒汉与饿汉 对于Singleton单例模式我们并不陌生,但我们常用的多是饿汉模式: Singleton实例的声明和实例化在instance()函数中同时完成. 而懒汉模式要求,Singleton实 ...

  3. lodash源码中debounce函数分析

    lodash源码中debounce函数分析 一.使用 在lodash中我们可以使用debounce函数来进行防抖和截流,之前我并未仔细注意过,但是不可思议的是,lodash中的防抖节流函数是一个函数两 ...

  4. Linux内核学习(五):linux kernel源码结构以及makefile分析

    Linux内核学习(五):linux kernel源码结构以及makefile分析 前面我们知道了linux内核镜像的生成.加载以及加载工具uboot. 这里我们来看看linux内核的源码的宏观东西, ...

  5. muduo源码client/server通信流程

    今天来学习一下muduo源码中client和server间的大致通信流程,以echo服务为例,先看一下echo对面的main函数代码. #include "examples/simple/e ...

  6. 【Muduo源码剖析笔记】 网络库之Acceptor、Connector

    [Muduo源码剖析笔记] 网络库之Acceptor.Connector Acceptor typedef std::function<void (int sockfd, const InetA ...

  7. muduo源码分析——TcpServer和Acceptor

    这篇文章用于分析muduo的TcpServer类和Acceptor类,原本打算将TcpConnection也放到这里一起聊的,但是那个太多啦,一篇文章太长会让人读的很不舒服把. 当然我用的代码是其他大 ...

  8. Caffe源码中common文件分析

    Caffe源码(caffe version:09868ac , date: 2015.08.15)中的一些重要头文件如caffe.hpp.blob.hpp等或者外部调用Caffe库使用时,一般都会in ...

  9. 从源码和内核角度分析redis和nginx以及java NIO可以支持多大的并发

    有人询问我网上一篇关于"redis为什么单线程这么快"的文章,我建议他不要看了,因为redis是单进程不是单线程,后面的意见不用看了,文章质量肯定不会很好,他也说了自己看了很久源码 ...

  10. 朋友问我学习高并发需不需要阅读源码,我是这样分析的!!

    来自:冰河技术 写在前面 最近正在写[高并发专题]的文章,其中,在[高并发专题]中,有不少是分析源码的文章,很多读者留言说阅读源码比较枯燥!问我程序员会使用框架了,会进行CRUD了,是否真的有必要阅读 ...

最新文章

  1. Numpy入门教程:练习作业01
  2. 【工具】24K纯吊丝超低成本低功耗电流测试方案,esp32实测功耗
  3. BlendMode类
  4. Angular4.0.0正式发布,附新特性及升级指南
  5. 前端学习(2081):三种方案对比es5没有闭包
  6. 前端学习(1534):服务器和客户端传输
  7. Linux开发相关书籍
  8. 使用序列化反序列化实现学生管理系统
  9. 蔡崇信完成对布鲁克林篮网和巴克莱中心的全资收购
  10. 基于人脸识别的课堂签到管理系统(五)---启动/结束签到,以及在百度智能云创建用户组
  11. ruby gem 记录
  12. Project 制作工作进度计划 排除休息日
  13. VirtualBox 中安装 Win10
  14. 网站弹窗广告html,网站弹窗广告(彻底关闭浏览器的广告弹窗?)
  15. 统一网络存储NAS+SAN=FAS
  16. 儿童定位手表、定位器、老人健康手表的工作原理
  17. iOS底层原理:weak的实现原理
  18. eclips下第一个java程序 hello world!
  19. Web安全之《SSH暴力破解》
  20. lcd屏幕有mipi接口吗_液晶屏mipi接口能用的最大分辨率是多少

热门文章

  1. 奔走相告!2020阿里云618年中大促火爆来袭!
  2. 微信小程序:页面路由
  3. STM32F407传输OV2640视频数据并在PC端显示
  4. YYT 0664 - 2008 医疗器械软件 软件生存周期过程
  5. 腾讯2021校园招聘-后台综合-第二次笔试 Apare_xzc
  6. Diffusion models代码解读:入门与实战
  7. 软件工程实训有必要吗_软件工程专业实训心得体会
  8. Manim文档及源码笔记-CE文档-示例库3使用Manim绘图
  9. 【终极!!!】cannot connect to X server, couldnt connect to display
  10. 操作系统2015(四川大学软件学院)