懒汉式单例的多线程问题

为什么说懒汉式遇上多线程会出现问题?
比如我们在Singleton的构造函数中加上sleep(1000),当第一个线程到来时判断为NULL,去创建,并中间挂起;当第二个线程到来时,仍旧判断为NULL,依次类推,这就不是单个实例了。
// 新建一个win32项目,因为要使用系统提供的API函数创建多线程
// win32控制台不能使用系统的API函数
#include "stdafx.h"

//控制台项目
#include "windows.h"
#include "winbase.h"
#include <process.h>

#include <iostream>
using namespace std;

// 构造函数不是线程安全函数
class Singelton
{
private:
	Singelton()
	{
		cout << "Singelton构造函数Begin" << endl;
		//Sleep(1000);     //睡眠
		cout << "Singelton构造函数End" << endl;
	}

public:
	static Singelton *getSingelton()
	{
		if (single == NULL) //需要判断
		{
			count++;
			single = new Singelton();
		}
		return single;
	}

	static Singelton *releaseSingelton()
	{
		if (single != NULL) //需要判断
		{
			delete single;
			single = NULL;
		}
		return single;
	}
	static void pirntS()    //测试函数
	{
		cout << "Singelton printS test" << endl;
	}

private:
	static Singelton *single;
	static int count;
};

//静态变量类外初始化  
Singelton *Singelton::single = NULL;  // 懒汉式
int Singelton::count = 0;

void main11()
{
	Singelton *s1 = Singelton::getSingelton();
	Singelton *s2 = Singelton::getSingelton();
	if (s1 == s2)
	{
		cout << "ok....equal" << endl;
	}
	else
	{
		cout << "not...equal" << endl;
	}
}

void MyThreadFunc(void *)
{
	//cout << "我是线程体 ...." << endl;  //cout << endl; 等价于: cout << '\n' << flush;刷新缓冲区
	cout << "我是线程体 ....\n";
	Singelton::getSingelton()->pirntS();
}

int _tmain(int argc, _TCHAR* argv[])
{
	//main11();
	HANDLE hThread[10];

	for (int i = 0; i<3; i++) //在主进程创建三个线程
	{
		hThread[i] = (HANDLE)_beginthread(MyThreadFunc, 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)                 //第一次检查
		{   
			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.


你可能感兴趣的:(设计模式,多线程,懒汉式)