muduo源码分析2——Singleton分析
1. 一般singleton写法
单例模式即要求只有在第一次调用的时候创建该对象,主要分为以下两条路(返回指针还是引用),返回引用可以防止使用中delete instance导致对象被提前销毁:
- private包含static指针以及构造函数,public里创建函数,通过调用其构造函数,返回该指针
- private包含static指针以及构造函数,public里创建函数,通过调用其构造函数,返回该引用
- private包含构造函数,public里创建函数,该函数里设置static变量实例,返回其引用
实际写单例的时候,需要考虑两个问题
- 是否需要delete,如何delete
- 是否考虑竞争
如果使用一般的创建函数,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主要做了以下动作
- 返回&
- 使用atexit,在main函数返回或者exit时,完成delete调用,完成栈清理
- 使用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有如下特点
- 使用!t_value_判断是否null
- 在pthread_key_create里,注册destructor,线程结束时调用,完成栈清理
- 返回&
- 保证只有一个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分析相关推荐
- Colly源码解析——结合例子分析底层实现
通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...
- muduo源码剖析——Singleton单例模式之懒汉模式与DCL双重检查
0 懒汉与饿汉 对于Singleton单例模式我们并不陌生,但我们常用的多是饿汉模式: Singleton实例的声明和实例化在instance()函数中同时完成. 而懒汉模式要求,Singleton实 ...
- lodash源码中debounce函数分析
lodash源码中debounce函数分析 一.使用 在lodash中我们可以使用debounce函数来进行防抖和截流,之前我并未仔细注意过,但是不可思议的是,lodash中的防抖节流函数是一个函数两 ...
- Linux内核学习(五):linux kernel源码结构以及makefile分析
Linux内核学习(五):linux kernel源码结构以及makefile分析 前面我们知道了linux内核镜像的生成.加载以及加载工具uboot. 这里我们来看看linux内核的源码的宏观东西, ...
- muduo源码client/server通信流程
今天来学习一下muduo源码中client和server间的大致通信流程,以echo服务为例,先看一下echo对面的main函数代码. #include "examples/simple/e ...
- 【Muduo源码剖析笔记】 网络库之Acceptor、Connector
[Muduo源码剖析笔记] 网络库之Acceptor.Connector Acceptor typedef std::function<void (int sockfd, const InetA ...
- muduo源码分析——TcpServer和Acceptor
这篇文章用于分析muduo的TcpServer类和Acceptor类,原本打算将TcpConnection也放到这里一起聊的,但是那个太多啦,一篇文章太长会让人读的很不舒服把. 当然我用的代码是其他大 ...
- Caffe源码中common文件分析
Caffe源码(caffe version:09868ac , date: 2015.08.15)中的一些重要头文件如caffe.hpp.blob.hpp等或者外部调用Caffe库使用时,一般都会in ...
- 从源码和内核角度分析redis和nginx以及java NIO可以支持多大的并发
有人询问我网上一篇关于"redis为什么单线程这么快"的文章,我建议他不要看了,因为redis是单进程不是单线程,后面的意见不用看了,文章质量肯定不会很好,他也说了自己看了很久源码 ...
- 朋友问我学习高并发需不需要阅读源码,我是这样分析的!!
来自:冰河技术 写在前面 最近正在写[高并发专题]的文章,其中,在[高并发专题]中,有不少是分析源码的文章,很多读者留言说阅读源码比较枯燥!问我程序员会使用框架了,会进行CRUD了,是否真的有必要阅读 ...
最新文章
- Numpy入门教程:练习作业01
- 【工具】24K纯吊丝超低成本低功耗电流测试方案,esp32实测功耗
- BlendMode类
- Angular4.0.0正式发布,附新特性及升级指南
- 前端学习(2081):三种方案对比es5没有闭包
- 前端学习(1534):服务器和客户端传输
- Linux开发相关书籍
- 使用序列化反序列化实现学生管理系统
- 蔡崇信完成对布鲁克林篮网和巴克莱中心的全资收购
- 基于人脸识别的课堂签到管理系统(五)---启动/结束签到,以及在百度智能云创建用户组
- ruby gem 记录
- Project 制作工作进度计划 排除休息日
- VirtualBox 中安装 Win10
- 网站弹窗广告html,网站弹窗广告(彻底关闭浏览器的广告弹窗?)
- 统一网络存储NAS+SAN=FAS
- 儿童定位手表、定位器、老人健康手表的工作原理
- iOS底层原理:weak的实现原理
- eclips下第一个java程序 hello world!
- Web安全之《SSH暴力破解》
- lcd屏幕有mipi接口吗_液晶屏mipi接口能用的最大分辨率是多少
热门文章
- 奔走相告!2020阿里云618年中大促火爆来袭!
- 微信小程序:页面路由
- STM32F407传输OV2640视频数据并在PC端显示
- YYT 0664 - 2008 医疗器械软件 软件生存周期过程
- 腾讯2021校园招聘-后台综合-第二次笔试 Apare_xzc
- Diffusion models代码解读:入门与实战
- 软件工程实训有必要吗_软件工程专业实训心得体会
- Manim文档及源码笔记-CE文档-示例库3使用Manim绘图
- 【终极!!!】cannot connect to X server, couldnt connect to display
- 操作系统2015(四川大学软件学院)