设计模式之:单例模式

来源:互联网 发布:lua调用linux c库 编辑:程序博客网 时间:2024/05/23 18:43

一个单例类的特点:

1、进程内只有唯一的类实例
不可以被随意构造(禁止任何形式的自行构造),在被使用时或使用前创建(前者为所谓懒汉式,后者为所谓饿汉式),进程结束时释放
2、多线程共用
内部资源访问控制需要锁互斥

具体的实现

1、实现禁止构造:
出于不可以被随意构造(禁止任何形式的自行构造)的需要,单例基类需要禁止构造,实现方式为继承一个noncopyable基类
可以像下面这样的实现一个noncopyable,也可以继承boost的boost::noncopyable,推荐下面的方式。
下面的方式也是c++11对delete关键字的运用,禁止掉默认、复制构造函数和赋值运算符方法,使程序中无法创建单例类的实例。
class noncopyable {public:    noncopyable() = delete;    noncopyable(const noncopyable &) = delete;    noncopyable &operator= (const noncopyable &) = delete;    ~noncopyable() = default;};

2、实现单例基类:
按常用的懒汉式,一个单例基类的基本特性:
1.  在被使用时创建,多个线程使用也只会创建一次;
2.  进程退出时,释放掉实例
基于以上特性需要,实现方式分别为:
1.  使用pthread_once_t数据结构,保证在第一次使用时,多个线程只会有一个线程实际创建单例实例,其余线程则直接获取创建好的实例
2.  让每个单例类实例以静态指针变量存在,并重载类静态资源析构函数(__attribute__((destruct))修饰),这样,在进程退出自动调用静态变量析构方法时,释放掉单例内存

具体实现如下,
1.  模板类Singleton继承noncopyable实现禁止构造
2.  public方法仅包括get,进程内线程只可以通过get方法获取单例类实例
3.  单例实例为类静态变量_instance,即每个单例类都有一个唯一的静态类实例
4.  同时每个单例类由静态变量_p_once控制只有一个线程会在单例类实例不存在时创建实例(调用类静态函数_construct),其他线程直接获取实例
5.  重载类的静态资源析构函数(__attribute__((destruct))修饰),在进程退出时,释放掉单例实例
template<class T> class Singleton : public noncopyable {    Singleton(){}    ~Singleton(){}    static void _construct () {        _instance = new T;    }    __attribute__((destruct)) static void _delete () {        delete _instance;    }    static T *_instance;    static pthread_once_t _p_once;public:    static T * get () {        pthread_once(&Singleton::_p_once, &Singleton::_construct);        return _instance;    }};template<class T> pthread_once_t Singleton<T>::_p_once = PTHREAD_ONCE_INIT;template<class T> T *Singleton<T>::_instance = nullptr;

3、实现一个单例类:
多线程均通过get方法获取单例实例即可。
由于多线程均操作同一个实例,所以单例类在资源非原子操作时要加互斥锁,如下:
class Resource {    int a;    std::atomic<int> b;    std::mutex mtx;public:    Resource(){}    ~Resource(){}    void Init () {        std::cout << "init" << std::endl;    }    void Set (int _a) {        std::unique_lock<std::mutex> lock(mtx);        a = _a;    }    int Get () {        int t;        {            std::unique_lock<std::mutex> lock(mtx);            t = a;        }        return t;    }    void SetAtomic (int _b) {        b = _b;    }    int GetAtomic () {        return b;    }};

4、测试:
    std::vector<std::thread> ths(10);    for (int i = 0; i < 10; i++) {        ths[i] = std::thread([i] () {            while (1) {                std::random_device rd;                int a = rd() % 100, b = rd() % 100;                Resource *s = Singleton<Resource>::get();                int rawa = s->Get(), rawb = s->GetAtomic();                s->Set(a);                s->SetAtomic(b);                std::cout << "i am thread " << i << " get rawa " << rawa << " and get rawb " << rawb << " , set a " << a << " and set b " << b << "-----------------" << std::endl;                std::chrono::microseconds duration(100);                std::this_thread::sleep_for(duration);            }        });    }    for (auto &i:ths) {        i.join();    }

10个线程并发的读写单例类Resource的单例。
下图是测试的输出情况,验证是否发生多线程同步问题。
i am thread 5 get rawa 40 and get rawb 38 , set a 49 and set b 65-----------------i am thread 1 get rawa 49 and get rawb 65 , set a 43 and set b 25-----------------i am thread 3 get rawa 43 and get rawb 25 , set a 14 and set b 87-----------------i am thread 8 get rawa 14 and get rawb 87 , set a 46 and set b 54-----------------i am thread 7 get rawa 46 and get rawb 54 , set a 63 and set b 0-----------------i am thread 6 get rawa 63 and get rawb 0 , set a 21 and set b 26-----------------i am thread 5 get rawa 21 and get rawb 26 , set a 35 and set b 3-----------------i am thread 9 get rawa 35 and get rawb 3 , set a 57 and set b 39-----------------i am thread 2 get rawa 57 and get rawb 39 , set a 24 and set b 16-----------------i am thread 0 get rawa 24 and get rawb 16 , set a 94 and set b 65-----------------i am thread 1 get rawa 94 and get rawb 65 , set a 30 and set b 0-----------------i am thread 3 get rawa 30 and get rawb 0 , set a 76 and set b 35-----------------i am thread 8 get rawa 76 and get rawb 35 , set a 46 and set b 14-----------------i am thread 7 get rawa 46 and get rawb 14 , set a 93 and set b 47-----------------i am thread 6 get rawa 93 and get rawb 47 , set a 56 and set b 90-----------------i am thread 4 get rawa 56 and get rawb 90 , set a 81 and set b 35-----------------i am thread 5 get rawa 81 and get rawb 35 , set a 74 and set b 20-----------------i am thread 9 get rawa 74 and get rawb 20 , set a 65 and set b 0-----------------i am thread 2 get rawa 65 and get rawb 0 , set a 56 and set b 62-----------------i am thread 0 get rawa 56 and get rawb 62 , set a 70 and set b 45-----------------i am thread 1 get rawa 70 and get rawb 45 , set a 39 and set b 62-----------------i am thread 3 get rawa 39 and get rawb 62 , set a 34 and set b 90-----------------i am thread 8 get rawa 34 and get rawb 90 , set a 33 and set b 57-----------------i am thread 7 get rawa 33 and get rawb 57 , set a 47 and set b 6-----------------i am thread 6 get rawa 47 and get rawb 6 , set a 6 and set b 1-----------------i am thread 4 get rawa 6 and get rawb 1 , set a 22 and set b 8-----------------i am thread 5 get rawa 22 and get rawb 8 , set a 65 and set b 72-----------------i am thread 9 get rawa 65 and get rawb 72 , set a 54 and set b 96-----------------i am thread 2 get rawa 54 and get rawb 96 , set a 79 and set b 59-----------------i am thread 0 get rawa 79 and get rawb 59 , set a 19 and set b 48-----------------i am thread 1 get rawa 19 and get rawb 48 , set a 99 and set b 12-----------------i am thread 3 get rawa 99 and get rawb 12 , set a 9 and set b 69-----------------i am thread 8 get rawa 9 and get rawb 69 , set a 97 and set b 16-----------------i am thread 7 get rawa 97 and get rawb 16 , set a 79 and set b 46-----------------i am thread 4 get rawa 73 and get rawb 95 , set a 45 and set b 7-----------------i am thread 5 get rawa 45 and get rawb 7 , set a 71 and set b 66-----------------