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

2024-02-25 03:52

本文主要是介绍[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

这篇关于[c++] 单例模式 + cyberrt TimingWheel 单例分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/744309

相关文章

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

C++变换迭代器使用方法小结

《C++变换迭代器使用方法小结》本文主要介绍了C++变换迭代器使用方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、源码2、代码解析代码解析:transform_iterator1. transform_iterat