单例模式

使用场景

需要全进程唯一实例时,往往会使用单例模式进行设计

唯一数据入口

假设一个对象负责更改本地设置,例如配置服务的网络端口号。如果出现两个或多个线程,每个线程拥有一个实例,线程并发共同进行配置。每个实例都在修改端口号。最终修改的结果就是不可控的。

共用的资源

现在我们假设有一个数据队列,由三个数据的生产者向队列push数据,一个消费者处理三个不同来源的数据。对队列进行pop操作。这就可能会涉及到四个线程并发的处理。怎样让这四个线程都能够获得到这个队列进行操作?全局变量?nonono不够优雅,这时候,使用单例模式。一个唯一的队列实例。不可以由其他对象创建,整个进程唯一,线程就能够通过这一唯一的实例进行数据的交互。

唯一的控制者

很多时候,我们需要对进程内的资源进行合理的调控,例如服务端进程在某一时刻,突然又大量数据或客户端请求进来。我们需要动态的增加数据处理线程,启用其他闲置的资源等等。这一时刻,决策者只能有一个。如果两个实例分别调控,最终的结果一定不是我们期望的。

实现

单例的实现有两种方式:懒汉、饿汉

懒汉方式

突出一个懒字。意思是只有在使用的时候才去实例化对象。以时间换空间,当访问量较小的时候,可以使用这种方式

为了保证线程安全,一般采用双检锁方法来创建对象

#include <iostream>
#include <pthread.h>


using namespace std;


class CLog
{
public:
    static CLog * getInstance();

private:
    CLog(){}
    CLog(const CLog & o) = delete;
    CLog & operator=(const CLog & o) = delete;

private:
    static CLog * instance_;
    static pthread_mutex_t mutex_;
};

CLog * CLog::instance_ = nullptr;
pthread_mutex_t CLog::mutex_ = PTHREAD_MUTEX_INITIALIZER;

CLog * CLog::getInstance()
{
    if (instance_ == nullptr)
    {
        pthread_mutex_lock(&mutex_);
        if (instance_ == nullptr)
        {
            // instance_ = new CLog(); 可能在执行new的过程中,对象的初始化还没有完成,但是instance_就不是nullptr了,这可能导致另外一个线程使用了未完全初始化的instance_,为避免这种情况,使用下面的方法
            CLog * tmp = new CLog();
            instance_ = tmp;
        }
        pthread_mutex_unlock(&mutex_);
    }

    return instance_;
}

int main()
{
    CLog * log = CLog::getInstance();

    return 0;
}

饿汉模式

对象还没有使用的时候就已经创建好了。这样以空间换时间

饿汉模式是线程安全的,因为不存在多线程实例化的问题

#include <iostream>


class CLog
{
public:
    static CLog * getInstance();

private:
    CLog() = default;
    ~CLog() = default;
    CLog(const CLog & o) = delete;
    CLog & operator=(const CLog & o) = delete;

private:
    static CLog * instance_;
};

CLog * CLog::instance_ = new CLog();

CLog * CLog::getInstance()
{
    return instance_;
}


int main()
{
    CLog * log = CLog::getInstance();

    return 0;
}
原文地址:https://www.cnblogs.com/zuofaqi/p/10277951.html