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

[c++] 单例模式 + cyberrt TimingWheel 单例分析

单例模式要求一个类在一个进程中只能创建一个对象。比如 cyberrt 中的 TimingWheel 类就是单例模式,这个类管理着一个进程内的所有定时器,只需要一个对象就可以。

单例模式的实现有两种方式,懒汉式和饿汉式。懒汉式,当第一次使用的时候才会真正创建这个对象;饿汉式,不管会不会用到这个对象,在进程启动的时候都会创建这个对象,如果一直不使用,那么就会造成资源浪费。饿汉式的缺点是可能造成资源浪费,但是对性能友好,因为在进程启动的时候就直接创建了,需要使用的时候可以直接拿来使用;懒汉式反之。

在工作中一般使用懒汉式。

1 懒汉式

懒汉式示例代码如下,在如下代码中实现了自动回收的机制,通过内部的类 Recycler 来完成。

#include <iostream>
#include <mutex>class Test {
public:static Test *GetInstance() {std::lock_guard<std::mutex> lock(mtx);if (instance == nullptr) {instance = new Test();return instance;}return instance;};Test(const Test &) = delete;Test &operator=(const Test &) = delete;~Test() {std::cout << "~Test()" << std::endl;};class Recycler {public:~Recycler() {if (Test::instance) {delete Test::instance;} else {std::cout << "no need to recycle" << std::endl;}}};static Recycler recycler;void Do() {std::cout << "Do()" << std::endl;}private:static Test *instance;static std::mutex mtx;Test() {std::cout << "Test()" << std::endl;};
};Test *Test::instance = nullptr;
std::mutex Test::mtx;
Test::Recycler recycler;void TestDo(Test test) {test.Do();
}int main() {Test *test = Test::GetInstance();test->Do();return 0;
}

特点:

(1)第一次使用对象的时候才会创建,懒加载模式。懒加载思想很常见,比如 linux 中用户态的内存管理,就是典型的懒加载。

(2)在 GetInstance() 需要加锁,如果多线程频繁调用,会影响性能。个人认为这个只是理论上的缺点,真正使用中,单例模式很少有多线程频繁调用的情况。

注意点:

(1)在 GetInstance() 中需要加锁。

(2)如下两个静态成员变量需要在类的外部初始化

类的静态变量需要在类外部初始化,这是静态变量和非静态变量的明显区别。

  static Test *instance;
  static std::mutex mtx;

(3)拷贝构造函数和赋值运算符需要禁用

如果不禁用,通过拷贝构造函数和赋值运算符可以生成新的对象,就不能保证单例了。

2 饿汉式

不管将来用不用,这个对象都会创建好。

#include <iostream>
#include <mutex>class Test {
public:static Test *GetInstance() {return instance;};Test(const Test &) = delete;Test &operator=(const Test &) = delete;~Test() {std::cout << "~Test()" << std::endl;};class Recycler {public:~Recycler() {if (Test::instance) {delete Test::instance;} else {std::cout << "no need to recycle" << std::endl;}}};static Recycler recycler;void Do() {std::cout << "Do()" << std::endl;}private:static Test *instance;Test() {std::cout << "Test()" << std::endl;};
};Test *Test::instance = new Test();
Test::Recycler recycler;char *p = (char *)malloc(1024);int main() {printf("main start\n");Test *test = Test::GetInstance();test->Do();printf("p: %p\n", p);p[0] = 1;return 0;
}

题外话:

从上边的代码实现中可以看出来,在 c++ 中,在函数外部是可以调用 new 来创建对象的,这种使用方式是自己很少使用的。

并且在函数外部也可以是有 malloc() 来申请内存。

但是在 c 中,在函数外部申请内存的话,如下代码所示,编译会报错。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>const char *p = (char *)malloc(1024);
int main() {printf("p: %p\n", p);p[0] = 1;return 0;
}

3 cyberrt 中 TimingWheel 单例实现

cyberrt 中的类 TimingWheel 使用了单例模式。TimingWheel 是一个进程内所有定时器的底层管理者。cyberrt 中实现单例的方式封装在了一个宏里边,这个宏是 DECLARE_SINGLETON,定义如下,实现主要有以下几点。

(1)使用 std::call_once 来实现,保证了原子性

(2)禁用了拷贝构造函数和赋值构造函数

#ifndef DISALLOW_COPY_AND_ASSIGN
#define DISALLOW_COPY_AND_ASSIGN(classname) \classname(const classname &) = delete;    \classname &operator=(const classname &) = delete;
#endif#ifndef DECLARE_SINGLETON
#define DECLARE_SINGLETON(classname)                                        \public:                                                                    \static classname *instance(bool create_if_needed = true) {                \static classname *inst = nullptr;                                       \if (!inst && create_if_needed) {                                        \static std::once_flag flag;                                           \std::call_once(flag, [&] { inst = new (std::nothrow) classname(); }); \}                                                                       \return inst;                                                            \}                                                                         \\static void clean_up() {                                                  \auto inst = instance(false);                                            \if (inst != nullptr) {                                                  \call_shut_down(inst);                                                 \}                                                                       \}                                                                         \\private:                                                                   \classname();                                                              \DISALLOW_COPY_AND_ASSIGN(classname)
#endif

相关文章:

  • android pdf框架-4,分析barteksc/PdfiumAndroid源码1
  • ap和ac的工作原理
  • [Linux]文件基础-如何管理文件
  • 掌控互联网脉络:深入解析边界网关协议(BGP)的力量与挑战
  • vue3+js 实现记住密码功能
  • CSS @符规则(@font-face、@keyframes、@media、@scope等)
  • 《最新出炉》系列初窥篇-Python+Playwright自动化测试-26-处理单选和多选按钮-下篇
  • 数据库应用:Windows 部署 MySQL 8.0.36
  • 字符串(算法竞赛)--Manacher(马拉车)算法
  • Unity3D MVC开发模式与开发流程详解
  • QT/自定义槽和信号
  • Sentinel微服务流量治理组件实战上
  • SQL语法法则
  • Cover和contain属性
  • 算法沉淀——动态规划之简单多状态 dp 问题(上)(leetcode真题剖析)
  • php的引用
  • [数据结构]链表的实现在PHP中
  • 0基础学习移动端适配
  • Angular数据绑定机制
  • CentOS6 编译安装 redis-3.2.3
  • C语言笔记(第一章:C语言编程)
  • JavaScript创建对象的四种方式
  • JavaScript设计模式之工厂模式
  • javascript数组去重/查找/插入/删除
  • Java反射-动态类加载和重新加载
  • java取消线程实例
  • Linux Process Manage
  • maven工程打包jar以及java jar命令的classpath使用
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • python_bomb----数据类型总结
  • react-native 安卓真机环境搭建
  • Spark学习笔记之相关记录
  • swift基础之_对象 实例方法 对象方法。
  • vue2.0项目引入element-ui
  • 数据仓库的几种建模方法
  • 温故知新之javascript面向对象
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • mysql面试题分组并合并列
  • 从如何停掉 Promise 链说起
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • (Note)C++中的继承方式
  • (第一天)包装对象、作用域、创建对象
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (转)Oracle存储过程编写经验和优化措施
  • (转载)Linux网络编程入门
  • .NET Core 和 .NET Framework 中的 MEF2
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .NET Core引入性能分析引导优化
  • .net 桌面开发 运行一阵子就自动关闭_聊城旋转门家用价格大约是多少,全自动旋转门,期待合作...
  • .net开源工作流引擎ccflow表单数据返回值Pop分组模式和表格模式对比
  • .net实现头像缩放截取功能 -----转载自accp教程网