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