C++ 多线程下的单例模式

/*
    代码中经常会使用到单例模式,单例模式就是隐藏构造函数,提供获取一个实例的静态方法。
    但是在多线程场景下,单例模式会有一些不同。例如Config类的instance方法如下
    */
    //获取一个实例(对外接口)
    static Config * instance()
    {
        if (NULL == m_instance)
        {
            //加锁(多线程场景下)
            m_mutex.acquire();
            if (NULL == m_instance)
            {
                m_instance = new Config();
                /*
                类似于这个类的初始化,我是不建议放在这里的,
                因为如果init方法执行失败,但是实例仍然不为NULL,
                建议在main函数中第一次调用instance方法时,执行init方法初始化实例
                (注意init方法也是只能执行一次的,请考虑多线程场景)

                init方法放在这个纯粹是为了解释这个场景下单例的使用
                */
                m_instance->init();
            }
            //解锁
            m_mutex.release();
        }
        return m_instance;
    }
/*
如果在instance方法中不加锁,在多线程的场景下,有可能创建出多个实例。
instance方法在加锁之后,还是有问题的。
假设线程A正在执行m_instance->init()方法(init方法执行的时间很长),此时线程B开始执行instance方法,
发现m_instance != NULL(因为线程A已经构造了该实例),那么线程B就会直接获取到这个并没有执行完init方法的m_instance实例,
线程B用这个m_instance去执行操作,就会出现问题。
*/

 

/*
简单的改进就是,去掉前边的判断,直接加锁,这样就避免了该问题,
线程B进行进来的时候因为线程A已经获取到锁,线程B会等待,
等到线程A释放锁之后(所有初始化操作已经完成),线程B判断m_instance != NULL,
线程B可以使用m_instance这个实例了
*/
static Config * Config::instance()
{
    //加锁(多线程场景下)
    m_mutex.acquire();
    if (NULL == m_instance)
    {
        m_instance = new Config();
        m_instance->init();
    }
    //解锁
    m_mutex.release();
return m_instance; }
/*
但是这样的改进会出现一个新问题,就是每次调用这个单例就会加锁判断,频繁调用会影响速度
*/

 

/*
    再次改进方案,使用一个临时变量构造,初始化,成功后再赋值给m_instance
    这样避免了多线程操作影响,又不影响速度
    */
    static Config * instance()
    {
        if (NULL == m_instance)
        {
            //加锁(多线程场景下)
            m_mutex.acquire();
            if (NULL == m_instance)
            {
                Config * pInstance = new Config();
                pInstance->init();
                m_instance = pInstance;
            }
            //解锁
            m_mutex.release();
        }
        return m_instance;
    }

 

posted on 2017-09-17 11:01  寒魔影  阅读(972)  评论(0编辑  收藏  举报

导航