转载

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

单例懒汉式和多线程问题


作为单例模式,是在整个程序运行期间只会建立一份内存空间,为了达到这个目标
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


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


正文到此结束
Loading...