C++单例懒汉式和多线程问题(MUTEX 保护)

简介: 单例懒汉式和多线程问题 作为单例模式,是在整个程序运行期间只会建立一份内存空间,为了达到这个目标 1、需要将构造函数设置为私有成员 2、需要一个私有的静态指针指向自身 3、需要一个公有的静态函数将这个上面的静态指针露出来 如下的代码就是一个懒汉式的单例 ...
单例懒汉式和多线程问题


作为单例模式,是在整个程序运行期间只会建立一份内存空间,为了达到这个目标
1、需要将构造函数设置为私有成员
2、需要一个私有的静态指针指向自身
3、需要一个公有的静态函数将这个上面的静态指针露出来

如下的代码就是一个懒汉式的单例

点击(此处)折叠或打开

  1. #include<iostream>
  2. using namespace std;

  3. class single_ins
  4. {
  5. private:
  6.     int a;
  7.     int b;
  8.     single_ins()
  9.     {
  10.         a= 0;
  11.         b= 0;
  12.     }
  13.     static single_ins* myc;
  14. public:
  15.     void setval(const int& a,const int& b)
  16.     {
  17.         this->a = a;
  18.         this->b = b;
  19.     }

  20.     void print()
  21.     {
  22.       cout<<"a:"<<a<<endl;
  23.       cout<<"b:"<<b<<endl;
  24.     }


  25.     static single_ins* setp()
  26.     {
  27.         //?
  28.         if(myc == NULL)
  29.         {
  30.             myc = new single_ins;
  31.         }
  32.         //?
  33.         return myc;
  34.     }

  35.     static void pfree()
  36.     {
  37.         if(myc != NULL)
  38.         {
  39.             delete myc;
  40.             myc = NULL;
  41.         }
  42.     }

  43. };

  44. //? init static value
  45. single_ins* single_ins::myc = NULL;
  46. //nit static value
  47. //single_ins* single_ins::myc = new single_ins;

  48. int main()
  49. {
  50.     single_ins* a = single_ins::setp();
  51.     single_ins* b = single_ins::setp();
  52.     a->setval(10,20);
  53.     b->print();

  54.     cout<<a<<" "<<b<<endl;
  55.     single_ins::pfree();

  56. }
但是上面的代码有明显的问题,就是遇到多线程的情况下,因为多个线程如果同事创建内存,由于彼此之间
并不能及时检查到内存已经分配,会分配多个内存,这个时候我们至少需要一个线程间同步手段来让他们之间
串行的执行,这个时候就涉及到两次检查(double check)
如果没有mutex保护就是下面这个程序:

点击(此处)折叠或打开

  1. #include <iostream>
  2. #include <unistd.h>
  3. using namespace std;


  4. //单列模式
  5. class single_ins
  6. {
  7.         private:
  8.                 int a;
  9.                 int b;
  10.                 single_ins()
  11.                 {
  12.                         cout<<"con begin\n";
  13.                         a= 0;
  14.                         b= 0;
  15.                         sleep(10); //故意拖长构造函数执行时间,造成懒汉式多线程问题
  16.                         cout<<"con end\n";
  17.                 }
  18.                 static single_ins* myc;//单例需要一个静态指针
  19.                 static int cnt;//构造调用次数统计
  20.         public:
  21.                 void setval(const int& a,const int& b)
  22.                 {
  23.                         this->a = a;
  24.                         this->b = b;
  25.                 }

  26.                 void print()
  27.                 {
  28.                         cout<<"a:"<<a<<endl;
  29.                         cout<<"b:"<<b<<endl;
  30.                         cout<<cnt<<endl;
  31.                 }


  32.                 static single_ins* setp() //函数获得指针值赋给静态成员指针变量
  33.                 {
  34.                         //懒汉式
  35.                         if(myc == NULL)
  36.                         {
  37.                                 myc = new single_ins;
  38.                                 cnt++;
  39.                         }
  40.                         //懒汉式
  41.                         return myc;
  42.                 }
  43.                 static void pfree()
  44.                 {
  45.                         if(myc != NULL)
  46.                         {
  47.                                 delete myc;
  48.                                 myc = NULL;
  49.                         }
  50.                 }
  51. };

  52. //懒汉式 init static value
  53. single_ins* single_ins::myc = NULL;
  54. int single_ins::cnt = 0;
  55. //饿汉试 init static value
  56. //single_ins* single_ins::myc = new single_ins;
  57. /*
  58.    懒汉式的问题在于多线程调用的时候会出现问题,很可能同时建立出多个内存空间,
  59.    而不是单列了。
  60.    */
  61. void* main21(void* argc)
  62. {
  63.         single_ins* inp = (single_ins*)argc;

  64.         inp = single_ins::setp();
  65.         inp->setval(10,20);
  66.         inp->print();
  67.         cout<<inp<<"\n";
  68.         return NULL;
  69. }


  70. int main(void)
  71. {
  72.         pthread_t tid;
  73.         single_ins* a[3] = {NULL};
  74.         void* tret[3] = {NULL};
  75.         for(int i = 0 ; i<3; i++)
  76.         {
  77.                 pthread_create(&tid,NULL,main21,(void*)a[i]);
  78.                 //pthread_join(tid, &(tret[i]));
  79.         }
  80.         sleep(50);
  81.         single_ins::pfree();

  82. }
会跑出结果
con begin
con begin
con begin
con end
a:10
b:20
1
0x7fc3880008c0
con end
a:10
b:20
2
0x7fc3800008c0
con end
a:10
b:20
3
0x7fc3840008c0


可以看到
0x7fc3880008c0 0x7fc3800008c0 0x7fc3840008c0
明显是3个不同的内存空间 这就不对了,而且可以看到构造函数
调用了3次
为此我们使用mutex来保护临时

如下:
 static single_ins* setp() //函数获得指针值赋给静态成员指针变量
 39         {
 40             //懒汉式
 41             if(myc == NULL)
 42             {
 43                 pthread_mutex_lock(&counter_mutex); //mutex 保护临界区
 44                 if(myc == NULL) //两次检查
 45                 {
 46                     myc = new single_ins;                                                                                                                           
 47                     cnt++;
 48                 }
 49                 pthread_mutex_unlock(&counter_mutex); //mutex结束
 50             }


这样代码如下:

点击(此处)折叠或打开

  1. #include <iostream>
  2. #include <unistd.h>
  3. using namespace std;

  4. pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;

  5. //单列模式
  6. class single_ins
  7. {
  8.         private:
  9.                 int a;
  10.                 int b;
  11.                 single_ins()
  12.                 {
  13.                         cout<<"con begin\n";
  14.                         a= 0;
  15.                         b= 0;
  16.                         sleep(10); //故意拖长构造函数执行时间,造成懒汉式多线程问题
  17.                         cout<<"con end\n";
  18.                 }
  19.                 static single_ins* myc;//单例需要一个静态指针
  20.                 static int cnt;//构造调用次数统计
  21.         public:
  22.                 void setval(const int& a,const int& b)
  23.                 {
  24.                         this->a = a;
  25.                         this->b = b;
  26.                 }

  27.                 void print()
  28.                 {
  29.                         cout<<"a:"<<a<<endl;
  30.                         cout<<"b:"<<b<<endl;
  31.                         cout<<cnt<<endl;
  32.                 }


  33.                 static single_ins* setp() //函数获得指针值赋给静态成员指针变量
  34.                 {
  35.                         //懒汉式
  36.                         if(myc == NULL)
  37.                         {
  38.                                 pthread_mutex_lock(&counter_mutex); //mutex 保护临界区
  39.                                 if(myc == NULL) //两次检查
  40.                                 {
  41.                                         myc = new single_ins;
  42.                                         cnt++;
  43.                                 }
  44.                                 pthread_mutex_unlock(&counter_mutex); //mutex结束
  45.                         }
  46.                         //懒汉式
  47.                         return myc;
  48.                 }
  49.                 static void pfree()
  50.                 {
  51.                         if(myc != NULL)
  52.                         {
  53.                                 delete myc;
  54.                                 myc = NULL;
  55.                         }
  56.                 }
  57. };

  58. //懒汉式 init static value
  59. single_ins* single_ins::myc = NULL;
  60. int single_ins::cnt = 0;
  61. //饿汉试 init static value
  62. //single_ins* single_ins::myc = new single_ins;
  63. /*
  64.    懒汉式的问题在于多线程调用的时候会出现问题,很可能同时建立出多个内存空间,
  65.    而不是单列了。
  66.    */



  67. void* main21(void* argc)
  68. {
  69.         single_ins* inp = (single_ins*)argc;

  70.         inp = single_ins::setp();
  71.         inp->setval(10,20);
  72.         inp->print();
  73.         cout<<inp<<"\n";
  74.         return NULL;
  75. }


  76. int main(void)
  77. {
  78.         pthread_t tid;
  79.         single_ins* a[3] = {NULL};
  80.         void* tret[3] = {NULL};
  81.         for(int i = 0 ; i<3; i++)
  82.         {
  83.                 pthread_create(&tid,NULL,main21,(void*)a[i]);
  84.                 //pthread_join(tid, &(tret[i]));
  85.         }
  86.         sleep(50);
  87.         single_ins::pfree();

  88. }
跑出的结果如下:

con begin
con end
a:10a:10
b:20
1
0x7f21f40008c0


b:20
1
0x7f21f40008c0
a:10
b:20
1
0x7f21f40008c0


现在就是正常的了。所以懒汉试单例遇到多线程一定要注意,饿汉试没有问题。
当然这是一个小列子而已,线程安全是一个很大的话题,特别需要注意。

作者微信:

               
相关文章
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
249 1
C++ 多线程之初识多线程
|
缓存 安全 C++
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
1062 7
|
消息中间件 存储 安全
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
421 5
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
690 6
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
367 0
C++ 多线程之线程管理函数
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
11月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
440 12
|
9月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
235 0