创建型模式之单例模式

为什么使用单例模式

在应用系统开发中,我们常常有以下需求:
1.在多个线程之间,比如初始化一次socket资源;比如servlet环境,共享同一个资源或者操作同一个对象

2.在整个程序空间使用全局变量,共享资源

3.大规模系统中,为了性能的考虑,需要节省对象的创建时间等等。

因为Singleton模式可以保证为一个类只生成唯一的实例对象,所以这些情况,Singleton模式就派上用场了。

实现单例步骤

1.构造函数私有化

2.提供一个全局的静态方法(全局访问点)

3.在类中定义一个静态指针,指向本类的变量的静态变量指针

饿汉式单例和懒汉式单例

懒汉式

#include <iostream>
using namespace std;

//懒汉式
class  Singelton
{
private:
    Singelton()
    {
        m_singer = NULL;
        m_count = 0;
        cout << "构造函数Singelton ... do" << endl;
    }

public:
    static Singelton *getInstance()
    {
        if (m_singer == NULL )  //懒汉式:1 每次获取实例都要判断 2 多线程会有问题
        {
            m_singer = new Singelton;
        }
        return m_singer;
    }
    static void printT()
    {
        cout << "m_count: " << m_count << endl;
    }

private:
    static Singelton *m_singer;
    static int m_count;
};

Singelton *Singelton::m_singer = NULL;  //懒汉式 并没有创建单例对象
int Singelton::m_count = 0;


void main01_1()
{
    cout << "演示 懒汉式" << endl;
    Singelton *p1 = Singelton::getInstance(); //只有在使用的时候,才去创建对象。
    Singelton *p2 = Singelton::getInstance();
    if (p1 != p2)
    {
        cout << "不是同一个对象" << endl;
    }
    else
    {
        cout << "是同一个对象" << endl;
    }
    p1->printT();
    p2->printT();

    system("pause");
    return ;
}

饿汉式

//俄汉式

class  Singelton2
{
private:
    Singelton2()
    {
        m_singer = NULL;
        m_count = 0;
        cout << "构造函数Singelton ... do" << endl;
    }

public:
    static Singelton2 *getInstance()
    {
//      if (m_singer == NULL )
//      {
//          m_singer = new Singelton2;
//      }
        return m_singer;
    }
    static void Singelton2::FreeInstance()
    {
        if (m_singer != NULL)
        {
            delete m_singer;
            m_singer = NULL;
            m_count = 0;
        }
    }
    static void printT()
    {
        cout << "m_count: " << m_count << endl;
    }

private:
    static Singelton2 *m_singer;
    static int m_count;
};

Singelton2 *Singelton2::m_singer = new Singelton2; //不管你创建不创建实例,均把实例new出来
int Singelton2::m_count = 0;

void main()
{
    cout << "演示 饿汉式" << endl;

    Singelton2 *p1 = Singelton2::getInstance(); //只有在使用的时候,才去创建对象。
    Singelton2 *p2 = Singelton2::getInstance();
    if (p1 != p2)
    {
        cout << "不是同一个对象" << endl;
    }
    else
    {
        cout << "是同一个对象" << endl;
    }
    p1->printT();
    p2->printT();
    Singelton2::FreeInstance();
    Singelton2::FreeInstance();


    system("pause");
}

多线程下的懒汉式单例和饿汉式单例

1.”懒汉”模式虽然有优点,但是每次调用GetInstance()静态方法时,必须判断NULL == m_instance,使程序相对开销增大。
2.多线程中会导致多个实例的产生,从而导致运行代码不正确以及内存的泄露。
3.提供释放资源的函数

讨论: 这是因为C++中构造函数并不是线程安全的。
C++中的构造函数简单来说分两步:

第一步:内存分配
第二步:初始化成员变量
由于多线程的关系,可能当我们在分配内存好了以后,还没来得急初始化成员变量,就进行线程切换,另外一个线程拿到所有权后,由于内存已经分配了,但是变量初始化还 没进行,因此打印成员变量的相关值会发生不一致现象。

#include "stdafx.h"
#include "windows.h"
#include "winbase.h"
#include <process.h>
#include "iostream"

using namespace std;
class Singelton
{
private:
    Singelton()
    {
        count ++;
        cout<<"Singelton构造函数begin\n"<<endl;
        Sleep(1000);
        cout<<"Singelton构造函数end\n"<<endl;

    }
private:
    //防止拷贝构造和赋值操作
    Singelton(const Singelton &obj) { ;}
    Singelton& operator=(const Singelton &obj)  { ;}
public:
    static Singelton *getSingelton()
    {
        return single;
    }

    static Singelton *releaseSingelton()
    {
        if (single != NULL) //需要判断
        {
            cout<<"释放资源\n"<<endl;
            delete single;
            single = NULL;
        }
        return single;
    }
    void pirntS() //测试函数
    {
        printf("Singelton printS test count:%d \n", count);
    }

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

//note 静态变量类外初始化
Singelton *Singelton::single = new Singelton();
int Singelton::count = 0;

int _tmainTTT(int argc, _TCHAR* argv[])
{
    Singelton *s1 = Singelton::getSingelton();
    Singelton *s2 = Singelton::getSingelton();
    if (s1 == s2)
    {
        cout<<"ok....equal"<<endl;
    }
    else
    {
        cout<<"not.equal"<<endl;
    }
    s1->pirntS();
    Singelton::releaseSingelton();
    cout <<"hello...."<<endl;
    system("pause");
    return 0;
}

unsigned int threadfunc2(void *myIpAdd)
{
    int id = GetCurrentThreadId();
    printf("\n threadfunc%d \n", id);
    return 1;
}

void threadfunc(void *myIpAdd)
{
    int id = GetCurrentThreadId();
    printf("\n threadfunc%d \n", id);
     Singelton::getSingelton()->pirntS();
    return ;
}

 int _tmain(int argc, _TCHAR* argv[])
{
    int i = 0; 
    DWORD dwThreadId[201], dwThrdParam = 1;
    HANDLE hThread[201]; 
    int threadnum = 3;

    for (i=0; i<threadnum; i++)
    {
        //hThread[i] = (HANDLE)_beginthreadex( NULL, 0, &threadfunc, NULL, 0,&dwThreadId[i] );
        hThread[i] = (HANDLE)_beginthread(&threadfunc, 0 , 0 );
        if (hThread[i] == NULL)
        {
            printf("begin thread %d error!!!\n", i);
            break;
        }       
    }
    //等待所有的子线程都运行完毕后,才执行 这个代码
    for (i=0; i<threadnum; i++)
    {
        WaitForSingleObject( hThread[i], INFINITE );      
    }
    printf("等待线程结束\n");
    for (i=0; i<threadnum; i++)
    {
        //CloseHandle( hThread[i] );
    }
    Singelton::releaseSingelton();
    cout <<"hello...."<<endl;
    system("pause");
    return 0;
}

多线程下懒汉式单例的Double-Checked Locking优化

新建MFC对话框应用程序。
方便使用临界区类对象,同步线程

// MFC Diagram 应用程序
#include "stdafx.h"
#include "01单例优化.h"
#include "01单例优化Dlg.h"
#include "afxdialogex.h"

#include "iostream"
using namespace std;

//临界区
static CCriticalSection cs;
//man pthread_create() 
class Singleton
{
private:
    Singleton()
    {
        TRACE("Singleton begin\n");
        Sleep(1000);
        TRACE("Singleton end\n");

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

public:
    static void printV()
    {
        TRACE("printV..\n");
    }

//请思考;懒汉式的Double-Check是一个经典问题!为什么需要2次检查 “if(pInstance == NULL)”
场景:假设有线程1、线程2、线程3,同时资源竞争。
//1)第1个、2个、3个线程执行第一个检查,都有可能进入黄色区域(临界区)
//2)若第1个线程进入到临界区,第2个、第3个线程需要等待
//3)第1个线程执行完毕,cs.unlock()后,第2个、第3个线程要竞争执行临界区代码。
//4)假若第2个线程进入临界区,此时第2个线程需要再次判断 if(pInstance == NULL)”,若第一个线程已经创建实例;第2个线程就不需要再次创建了。保证了单例;
//5)同样道理,若第2个线程,cs.unlock()后,第3个线程会竞争执行临界区代码;此时第3个线程需要再次判断 if(pInstance == NULL)。通过检查发现实例已经new出来,就不需要再次创建;保证了单例。

    static Singleton *Instantialize()
    {
        if(pInstance == NULL)  //double check 
        {   
            cs.Lock(); //只有当pInstance等于null时,才开始使用加锁机制 二次检查
            if(pInstance == NULL)
            {
                pInstance = new Singleton(); 
            }
            cs.Unlock();
        }
        return pInstance;
    }
    static Singleton *pInstance;

};

Singleton* Singleton::pInstance = 0;

void CMy01单例优化Dlg::OnBnClickedButton1()
{
    CCriticalSection  cs;
    cs.Lock();

    cs.Unlock();
    // TODO: 在此添加控件通知处理程序代码
}

void threadfunc(void *myIpAdd)
{
    int id = GetCurrentThreadId();
    TRACE("\n threadfunc%d \n", id);
    Singleton::Instantialize()->printV();
    //Singelton::getSingelton()->pirntS();
}

void CMy01单例优化Dlg::OnBnClickedButton2()
{
    int i = 0; 
    DWORD dwThreadId[201], dwThrdParam = 1;
    HANDLE hThread[201]; 
    int threadnum = 3;

    for (i=0; i<threadnum; i++)
    {
        //hThread[i] = (HANDLE)_beginthreadex( NULL, 0, &threadfunc, NULL, 0,&dwThreadId[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 );    
    }
    TRACE("等待线程结束\n");
    for (i=0; i<threadnum; i++)
    {
        //CloseHandle( hThread[i] );
    }
    //Singelton::releaseSingelton();

    TRACE("ddddd\n");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tingzhushaohua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值