懒汉式单例模式的多线程问题

为什么说懒汉式遇上多线程会出现问题?
        比如我们在Singleton的构造函数中加上sleep(1000),当第一个线程到来时判断为NULL,去创建,并中间挂起;当第二个线程到来时,仍旧判断为NULL,依次类推,这就不是单个实例了。(也就是说,创建单例如果需要一定时间,这时在多线程情况下会出现“竞速创建”!(可能还有别的原因))
#include<iostream>
#include"windows.h"
#include"winbase.h"
#include<process.h>
using namespace std;

class SingletonBug
{
private:
    static SingletonBug* m_pBug;
    SingletonBug()
    {
        cout << "constructor Begin()" << endl;

        Sleep(3000);

        cout << "constructor end()" << endl;
    }

public:
    static SingletonBug* getInstance()
    {
        if (m_pBug == NULL)
        {
            m_pBug = new SingletonBug;
        }
        return m_pBug;
    }

    static void freeInstance()
    {
        if (m_pBug != NULL)
        {
            delete m_pBug;
            m_pBug = NULL;
        }
    }

    static void print()
    {
        cout << "I am Singleton" << endl;
    }
};

SingletonBug* SingletonBug::m_pBug = NULL;

//void main011()    //单线程
//{
//    SingletonBug* p1 = SingletonBug::getInstance();
//    SingletonBug* p2 = SingletonBug::getInstance();
//
//    if (p1 == p2)
//        cout << "only" << endl;
//    else
//        cout << "double" << endl;
//
//    SingletonBug::freeInstance();
//}


void myThreadPro(void *)
{
    cout << "线程体执行" << endl;
    SingletonBug::getInstance()->print();
}

int main()        //多线程
{
    HANDLE hThread[10];

    for (int i = 0; i < 3; i++)
    {
        hThread[i] = (HANDLE)_beginthread(myThreadPro, 0, NULL);
    }

    for (int i = 0; i < 3; i++)
    {
        WaitForSingleObject(hThread[i], INFINITE);
    }

    return 0;
}

从上例的运行结果看,Singelton()的构造函数执行了三次,不再是单例!



小插曲:——endl和"\n"的区别:

在MyThreadFunc()函数中,cout << "我是线程体 ...." << endl; //cout << endl; 等价于: cout << '\n' << flush;endl刷新缓冲区;。而cout << "我是线程体 ....\n";在C++里\n不刷新缓冲区。
void main()
{
	int n = 10;
	cout << n << '\n';   //operator <<(ostream&, char)
	cout << n << "\n";   //operator <<(ostream&, const char *) 
	cout << n << endl;   
	/*
	inline ostream &std::endl(ostream& OutStream)
        {
		OutStream.put('\n');
		OutStream.flush();
		return OutStream;
	}
	*/
}
       <<并不是后面跟着什么就直接输出到屏幕什么!cout代表后面的内容输出到 控制台的一个缓冲槽 ,而不是控制台(黑屏幕)。那么缓冲槽在什么情况下会把缓冲槽的内容输出到控制台的“黑屏幕界面”??当遇到endl或者其他fflush之类的命令或函数时,缓冲槽里的内容会按照顺序输出到控制台,再由控制台进行 转义字符 的识别打印。endl会换行、清槽——把缓冲槽里的内容输出到控制台。



懒汉式多线程同步:(优化上述代码)——使用临界区
新建MFC程序测试:

//临界区
static CCriticalSection cs;

class Singleton
{
private:
	Singleton()
	{
		m_count ++;
		TRACE("Singleton begin\n");
		Sleep(1000);
		TRACE("Singleton end\n");

	}
	Singleton(const Singleton &);
	Singleton& operator = (const Singleton &);

public:
	static void printV()
	{
		TRACE("printV..m_count:%d \n", m_count);
	}
	static Singleton *Instantialize()
	{
		if(pInstance == NULL)                 //第一次检查
		{   
// 之所以在对pInstance 是否为空做了两次判断,是因为该方法调用一次就产生了对象,pInstance == NULL 大部分情况下都为false,
// 如果每次获取实例就加锁,效率太低。而改进的方法只需要在第一次——调用的时候加锁,可大大提高效率。
			cs.Lock();                    //只有当pInstance等于null时,才开始使用加锁机制 二次检查
			if(pInstance == NULL)         //第二次检查
			{
				pInstance = new Singleton();
			}
			cs.Unlock();
		}
		return pInstance;
	}
	static Singleton *pInstance;
	static int m_count;

};

Singleton* Singleton::pInstance = NULL;      //懒汉式
int Singleton::m_count = 0;

//

void threadfunc(void *myIpAdd)
{
	Singleton::Instantialize()->printV();
}

void C懒汉式线程同步Dlg::OnBnClickedButton1()          //主线程
{
	int i = 0; 
	DWORD dwThreadId[201], dwThrdParam = 1;
	HANDLE hThread[201]; 
	int threadnum = 3;

	for (i=0; i<threadnum; i++)      //子线程
	{
		hThread[i] = (HANDLE)_beginthread(&threadfunc, 0 , 0 );
		if (hThread[i] == NULL)
		{
			TRACE("begin thread %d error!!!\n", i);
			break;
		}		
	}

	for (i=0; i<threadnum; i++)
	{
		WaitForSingleObject( hThread[i], INFINITE );	
	}

}

点击按钮,可以看到Singleton()构造函数只执行了一次,m_count的输出值自然也就均为1.

附:http://blog.csdn.net/hackbuteer1/article/details/7460019
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值