当前位置:   article > 正文

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

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

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

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

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

1 懒汉式

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

  1. #include <iostream>
  2. #include <mutex>
  3. class Test {
  4. public:
  5. static Test *GetInstance() {
  6. std::lock_guard<std::mutex> lock(mtx);
  7. if (instance == nullptr) {
  8. instance = new Test();
  9. return instance;
  10. }
  11. return instance;
  12. };
  13. Test(const Test &) = delete;
  14. Test &operator=(const Test &) = delete;
  15. ~Test() {
  16. std::cout << "~Test()" << std::endl;
  17. };
  18. class Recycler {
  19. public:
  20. ~Recycler() {
  21. if (Test::instance) {
  22. delete Test::instance;
  23. } else {
  24. std::cout << "no need to recycle" << std::endl;
  25. }
  26. }
  27. };
  28. static Recycler recycler;
  29. void Do() {
  30. std::cout << "Do()" << std::endl;
  31. }
  32. private:
  33. static Test *instance;
  34. static std::mutex mtx;
  35. Test() {
  36. std::cout << "Test()" << std::endl;
  37. };
  38. };
  39. Test *Test::instance = nullptr;
  40. std::mutex Test::mtx;
  41. Test::Recycler recycler;
  42. void TestDo(Test test) {
  43. test.Do();
  44. }
  45. int main() {
  46. Test *test = Test::GetInstance();
  47. test->Do();
  48. return 0;
  49. }

特点:

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

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

注意点:

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

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

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

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

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

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

2 饿汉式

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

  1. #include <iostream>
  2. #include <mutex>
  3. class Test {
  4. public:
  5. static Test *GetInstance() {
  6. return instance;
  7. };
  8. Test(const Test &) = delete;
  9. Test &operator=(const Test &) = delete;
  10. ~Test() {
  11. std::cout << "~Test()" << std::endl;
  12. };
  13. class Recycler {
  14. public:
  15. ~Recycler() {
  16. if (Test::instance) {
  17. delete Test::instance;
  18. } else {
  19. std::cout << "no need to recycle" << std::endl;
  20. }
  21. }
  22. };
  23. static Recycler recycler;
  24. void Do() {
  25. std::cout << "Do()" << std::endl;
  26. }
  27. private:
  28. static Test *instance;
  29. Test() {
  30. std::cout << "Test()" << std::endl;
  31. };
  32. };
  33. Test *Test::instance = new Test();
  34. Test::Recycler recycler;
  35. char *p = (char *)malloc(1024);
  36. int main() {
  37. printf("main start\n");
  38. Test *test = Test::GetInstance();
  39. test->Do();
  40. printf("p: %p\n", p);
  41. p[0] = 1;
  42. return 0;
  43. }

题外话:

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

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

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

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

3 cyberrt 中 TimingWheel 单例实现

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

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

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

  1. #ifndef DISALLOW_COPY_AND_ASSIGN
  2. #define DISALLOW_COPY_AND_ASSIGN(classname) \
  3. classname(const classname &) = delete; \
  4. classname &operator=(const classname &) = delete;
  5. #endif
  6. #ifndef DECLARE_SINGLETON
  7. #define DECLARE_SINGLETON(classname) \
  8. public: \
  9. static classname *instance(bool create_if_needed = true) { \
  10. static classname *inst = nullptr; \
  11. if (!inst && create_if_needed) { \
  12. static std::once_flag flag; \
  13. std::call_once(flag, [&] { inst = new (std::nothrow) classname(); }); \
  14. } \
  15. return inst; \
  16. } \
  17. \
  18. static void clean_up() { \
  19. auto inst = instance(false); \
  20. if (inst != nullptr) { \
  21. call_shut_down(inst); \
  22. } \
  23. } \
  24. \
  25. private: \
  26. classname(); \
  27. DISALLOW_COPY_AND_ASSIGN(classname)
  28. #endif
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/157601
推荐阅读
相关标签
  

闽ICP备14008679号