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


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

作者微信:

               
相关文章
|
1月前
|
缓存 安全 C++
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
41 7
|
1月前
|
消息中间件 存储 安全
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
52 1
C++ 多线程之初识多线程
|
2月前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
2月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
67 6
|
2月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
28 0
C++ 多线程之线程管理函数
|
20天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
29 2
|
26天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
62 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
68 4
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
77 4