zoukankan      html  css  js  c++  java
  • 单例模式共享数据分析

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

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

    // 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函数一次(也就是括号中的第二个参数),它需要一个标记(第一个参数)
  • 相关阅读:
    变量属性
    String类
    Random类
    Scanner类
    文本与文本域对齐
    Java list集合排序
    float属性影响后续元素排版问题
    查询满足条件的最新数据(逐步优化,mysql、达梦数据库)
    关于select下拉框选择触发事件
    JQuery获取父,子,兄弟节点
  • 原文地址:https://www.cnblogs.com/SunShine-gzw/p/13521045.html
Copyright © 2011-2022 走看看