您的位置:首页 > 其它

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

2015-10-08 09:56 267 查看
为什么说懒汉式遇上多线程会出现问题?

       比如我们在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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息