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


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

作者微信:

               
相关文章
|
30天前
|
设计模式 安全 测试技术
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
61 0
|
30天前
|
数据可视化 关系型数据库 编译器
【C/C++ 单线程性能分析工具 Gprof】 GNU的C/C++ 性能分析工具 Gprof 使用全面指南
【C/C++ 单线程性能分析工具 Gprof】 GNU的C/C++ 性能分析工具 Gprof 使用全面指南
104 2
|
27天前
|
存储 算法 Java
【C/C++ 线程池设计思路】 深入探索线程池设计:任务历史记录的高效管理策略
【C/C++ 线程池设计思路】 深入探索线程池设计:任务历史记录的高效管理策略
72 0
|
30天前
|
消息中间件 Linux 调度
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
67 0
|
27天前
|
安全 Java 调度
【C/C++ 线程池设计思路 】设计与实现支持优先级任务的C++线程池 简要介绍
【C/C++ 线程池设计思路 】设计与实现支持优先级任务的C++线程池 简要介绍
43 2
|
28天前
|
Linux API C++
【C++ 线程包裹类设计】跨平台C++线程包装类:属性设置与平台差异的全面探讨
【C++ 线程包裹类设计】跨平台C++线程包装类:属性设置与平台差异的全面探讨
51 2
|
28天前
|
设计模式 安全 C++
【C++ const 函数 的使用】C++ 中 const 成员函数与线程安全性:原理、案例与最佳实践
【C++ const 函数 的使用】C++ 中 const 成员函数与线程安全性:原理、案例与最佳实践
71 2
|
30天前
|
存储 监控 API
【C/C++ 文件操作】深入浸润:C++多线程文件操作的艺术与策略
【C/C++ 文件操作】深入浸润:C++多线程文件操作的艺术与策略
56 0
|
30天前
|
算法 安全 Unix
【C++ 20 信号量 】C++ 线程同步新特性 C++ 20 std::counting_semaphore 信号量的用法 控制对共享资源的并发访问
【C++ 20 信号量 】C++ 线程同步新特性 C++ 20 std::counting_semaphore 信号量的用法 控制对共享资源的并发访问
30 0
|
1月前
|
存储 开发框架 安全
【C++ 线程】深入理解C++线程管理:从对象生命周期到线程安全
【C++ 线程】深入理解C++线程管理:从对象生命周期到线程安全
86 0

热门文章

最新文章