设计模式之:单例模式

一个单例类的特点:

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-----------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值