单例模式共享数据分析

首先来看单例模式的实现:

所谓单例,就是对象的创建只能一次,也就是不能通过构造函数直接创建对象,要通过其他手段,下面请看代码:

// ConsoleApplication6.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include<thread>
#include<iostream>
#include<list>
#include<mutex>
using namespace std;
class A
{
private:
    A() {}
    static A* Instance;
public:
    static A* GetInstance()
    {
        if (Instance == NULL)
        {
            Instance = new A;
        }
        return Instance;
    }
};
A* A::Instance=NULL;
int main() { A * x = A::GetInstance(); A* y = A::GetInstance(); return 0; }

上面虽然企图生成两个对象,但最终结果返回的是一个对象的指针。

上述代码没有考虑对象释放的问题,现对代码进行改进,让其也自动释放不要的内存:

// ConsoleApplication6.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include<thread>
#include<iostream>
#include<list>
#include<mutex>
using namespace std;
class A
{
private:
    A() {}
    static A* Instance;
public:
    static A* GetInstance()
    {
        if (Instance == NULL)
        {
            Instance = new A;
            static A_Guard gl;//在这里生成了一个静态对象,在类A释放的时候,类A_Guard也会被释放
        }
        return Instance;
    }


    class A_Guard
    {
    public:
        ~A_Guard()
        {
            if (A::Instance!=NULL)
            {
                delete A::Instance;
                A::Instance = NULL;
            }
        }
    };
};
A* A::Instance=NULL;
int main()
{
A
* x = A::GetInstance();
A
* y = A::GetInstance();
return 0;
}

接下来我开对多个线程来创建该对象,然后调用对象的函数实现多线程技术:

// ConsoleApplication6.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include<thread>
#include<iostream>
#include<list>
#include<mutex>
using namespace std;
class A
{
private:
    A() {}
    static A* Instance;
public:
    static A* GetInstance()
    {
        if (Instance == NULL)
        {
            Instance = new A;
            static A_Guard gl;//在这里生成了一个静态对象,在类A释放的时候,类A_Guard也会被释放
        }
        return Instance;
    }
    void Test(int n)
    {
        cout << "测试:" << n << endl;
    }

    class A_Guard
    {
    public:
        ~A_Guard()
        {
            if (A::Instance!=NULL)
            {
                delete A::Instance;
                A::Instance = NULL;
            }
        }
    };
};
A* A::Instance = NULL;

void Thread_One(int n)
{
    A* x = A::GetInstance();
    x->Test(n);
}
void Thread_Two(int n)
{
    A* y = A::GetInstance();
    y->Test(n);
}
int main()
{
    thread th_one(Thread_One,1);
    thread th_two(Thread_Two,2);
    th_one.join();
    th_two.join();

    return 0;
}

上述的对象可能会被多次构造,扣取上诉代码片段:

1 if (Instance == NULL)
2         {
3             Instance = new A;
4             static A_Guard gl;//在这里生成了一个静态对象,在类A释放的时候,类A_Guard也会被释放
5         }

如果一个线程还没有执行第三行,另外一个线程也进来了,那是不是就不能保证对象只被创建了一次,于是可以试着加把锁试试看,接下来我对代码进行改进:

 1 // ConsoleApplication6.cpp : 定义控制台应用程序的入口点。
 2 #include "stdafx.h"
 3 #include<thread>
 4 #include<iostream>
 5 #include<list>
 6 #include<mutex>
 7 using namespace std;
 8 mutex mut_one;
 9 class A
10 {
11 private:
12     A() {}
13     static A* Instance;
14 public:
15     static A* GetInstance()
16     {
17         unique_lock<mutex>  m(mut_one);
18         if (Instance == NULL)
19         {
20             Instance = new A;
21             static A_Guard gl;//在这里生成了一个静态对象,在类A释放的时候,类A_Guard也会被释放
22         }
23         return Instance;
24     }
25     void Test(int n)
26     {
27         cout << "测试:" << n << endl;
28     }
29 
30     class A_Guard
31     {
32     public:
33         ~A_Guard()
34         {
35             if (A::Instance!=NULL)
36             {
37                 delete A::Instance;
38                 A::Instance = NULL;
39             }
40         }
41     };
42 };
43 A* A::Instance = NULL;
44 
45 void Thread_One(int n)
46 {
47     A* x = A::GetInstance();
48     x->Test(n);
49 }
50 void Thread_Two(int n)
51 {
52     A* y = A::GetInstance();
53     y->Test(n);
54 }
55 int main()
56 {
57     thread th_one(Thread_One,1);
58     thread th_two(Thread_Two,2);
59     th_one.join();
60     th_two.join();
61 
62     return 0;
63 }

这样好像就解决问题了,但真的这样吗?

仔细观察发现,对象的构建只需要一次,也就是说我只需要在对象创建的那时候加锁就可以了,创建好后根本没有必要加锁了,加锁虽然解决了对象创建的问题,但是大大降低了相率,接下来对代码进一步改进:

 1 // ConsoleApplication6.cpp : 定义控制台应用程序的入口点。
 2 #include "stdafx.h"
 3 #include<thread>
 4 #include<iostream>
 5 #include<list>
 6 #include<mutex>
 7 using namespace std;
 8 mutex mut_one;
 9 class A
10 {
11 private:
12     A() {}
13     static A* Instance;
14 public:
15     static A* GetInstance()
16     {
17         if (Instance == NULL)
18         {
19             unique_lock<mutex>  m(mut_one);
20             if (Instance == NULL)
21             {
22                 Instance = new A;
23                 static A_Guard gl;//在这里生成了一个静态对象,在类A释放的时候,类A_Guard也会被释放
24         
25             }
26         }29             return Instance;31     }
32     void Test(int n)
33     {
34         cout << "测试:" << n << endl;
35     }
36 
37     class A_Guard
38     {
39     public:
40         ~A_Guard()
41         {
42             if (A::Instance!=NULL)
43             {
44                 delete A::Instance;
45                 A::Instance = NULL;
46             }
47         }
48     };
49 };
50 A* A::Instance = NULL;
51 
52 void Thread_One(int n)
53 {
54     A* x = A::GetInstance();
55     x->Test(n);
56 }
57 void Thread_Two(int n)
58 {
59     A* y = A::GetInstance();
60     y->Test(n);
61 }
62 int main()
63 {
64     thread th_one(Thread_One,1);
65     thread th_two(Thread_Two,2);
66     th_one.join();
67     th_two.join();
68 
69     return 0;
70 }

多了一个17行了判断条件,与第20行组成双重判断,这样的话只有在最开始才会进入if条件,后面的情况都会跳出if执行else直接返回对象指针。

接下来引入call_once,也是为了解决上诉问题的,但是据说效率比mutex更高,接下来请看代码:

// ConsoleApplication6.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include<thread>
#include<iostream>
#include<list>
#include<mutex>
using namespace std;
mutex mut_one; 
once_flag gl_flag;//标记
class A
{
private:
    A() {}
    static A* Instance;
public:
    static A* CreateInstance()
    {
        Instance = new A;
        static A_Guard gl;
        return Instance;
    }
    static A* GetInstance()
    {
            call_once(gl_flag,CreateInstance);
            return Instance;

    }
    void Test(int n)
    {
        cout << "测试:" << n << endl;
    }

    class A_Guard
    {
    public:
        ~A_Guard()
        {
            if (A::Instance!=NULL)
            {
                delete A::Instance;
                A::Instance = NULL;
            }
        }
    };
};
A* A::Instance = NULL;

void Thread_One(int n)
{
    A* x = A::GetInstance();
    x->Test(n);
}
void Thread_Two(int n)
{
    A* y = A::GetInstance();
    y->Test(n);
}
int main()
{
    thread th_one(Thread_One,1);
    thread th_two(Thread_Two,2);
    th_one.join();
    th_two.join();

    return 0;
}

下面说说call_once的用法:

call_once(gl_flag,CreateInstance);表示只调用CreateInstance函数一次(也就是括号中的第二个参数),它需要一个标记(第一个参数)
原文地址:https://www.cnblogs.com/SunShine-gzw/p/13521045.html