一、实现的技术如下:
1、以对象管理资源(例如:临界区),即资源获得即初始化(RAII)。
2、防止异常逃出析构函数,即必须捕捉析构函数产生的异常。
3、恰好足够线程安全。
二、以对象管理资源:
1、不以对象管理资源分析:
以CCriticalSection类(见附录源码)为例分析如下:
(a)、该类可以实现临界区管理功能,具体功能如下:
<1>、临界区的初始化(Init())。
<2>、临界区上锁(Lock())。
<3>、临界区解锁(Unlock())。
<4>、临界区的删除(Term())。
(b)、该类的不足如下:
<1>、用户可能在没有初始化临界区之前使用该临界区。
例如:在没有初始化前,对临界区做上锁、解锁或者删除临界区,这都会使程序崩溃。
<2>、用户可能在删除临界区后继续使用该临界区。
例如:在临界区被删除后,对临界区做上锁或者解锁操作,这会导致程序崩溃。
<3>、用户可能忘记删除不再使用的临界区。
例如:在临界区不再使用后忘记删除,这会产生内存泄漏。
<4>、用户对临界区上锁后,可能忘记解锁或者在解锁前程序产生异常,这都可能导致该临界区一直处于上锁状态,使其他用户无法访问被临界区上锁的数据,这也会产生无法删除临界区的内存泄漏。
2、以对象管理资源分析如下:
(a)、以CAutoCriticalSection类(见附录源码)为例分析如下:
该类的可以实现CCriticalSection的功能,同时还可以克服该类的不足。
该类的优点如下:
<1>、保证在使用该临界区之前初始化改临界区。
使用的技术为:资源获得即初始化,即在该类的构造函数中初始化临界区。
<2>、不会产生因用户忘记删除临界区而产生内存泄漏。
实现方法为:在该函数的析构函数中删除不再使用的临界区。
<3>、在用户删除临界区后,不会再使用临界区。
实现方法为:在使用临界区前先判断该临界区是否还存在,若存在可以继续使用,若不存在就不再使用。
(b)、以CObjectLockT类(源码见附录)为例分析如下:
该类可以实现对CAutoCriticalSection类的管理,从而可以防止忘了解锁的危险。
实现方法:在该类的构造函数里对临界区上锁,在析构函数里对临界区解锁。
原因:当离开该类对象的作用域或者产生异常时都会调用该类对象的析构函数,因此可以保证干临界区的解锁。
三、析构函数和异常
1、析构函数不捕获异常
在析构函数产生异常时,若此时捕获就不再有捕获的时机,那么程序就会崩溃,用回也不会明白程序崩溃的原因。
2、在析构函数中捕获异常
在必须在析构函数捕获异常,当产生异常时捕获异常,从而可以记下产生异常的原因,还可以让程序正常终止。
3、 对外提供Term()函数的原因
既然在析构函数调用该函数,那么对外提供该函数的原因是什么?
<1>、给用户一个随时删除临界区的权利,可以实现当临界区不再使用时随时删除,节省内存的使用。
<2>、给用户提供该函数的目的是给用户一个自己处理错误的机会,若用户认为该函数不会产生异常或者错误,那么当在析构函数调用该函数产生错误或异常时用户将无法再抱怨我们没有给他处理错误的机会。
四、线程安全
通过给CObjectRootEx模板类传递不同的类(CMultiThreadModel和CSingleThreadModel)从而可以恰好足够线程安全的控制。
1、 以CSingleThreadModel类为CObjectRootEx类的模板参数。
当为客户的程序上锁,即调用Lock()时,那么最终调用的为CFakeCriticalSection类的Lock(),该类的Lock没有做任何实现,仅仅是简单返回。解锁也一样。
2、 以CMultiThreadModel类为CObjectRootEx类的模板参数。
当为客户的程序上锁,即调用Lock()时,那么最终调用的为CCriticalSection类的Lock(),从而实现对临界区的上锁,可以防止多个线程的同时访问。解锁也一样。
总结:
1、 若确信自己的程序不在多线程的环境下运行,那么可以使自己的程序继承CObjectRootEx类,并以CSingleThreadModel类为参数,这在现在看起来或许有点多余。
2、 直到某一天自己的程序要运行在多线环境下,那么只需要以CMultiThreadModel类替换CSingleThreadModel类,其他的什么也不用变化,就可以实现线程同步访问公共数据。
附录:
/
//Asynchronous code
#ifndef ASYN_H_
#define ASYN_H_
class CCriticalSection
{
public:
CCriticalSection()throw()
{
memset(&m_sec, 0, sizeof(CRITICAL_SECTION));
}
virtual ~CCriticalSection()
{
}
HRESULT Lock()throw()
{
EnterCriticalSection(&m_sec);
return S_OK;
}
HRESULT Unlock()throw()
{
LeaveCriticalSection(&m_sec);
return S_OK;
}
HRESULT Init()throw()
{
HRESULT hRes = E_FAIL;
try
{
InitializeCriticalSection(&m_sec);
hRes = S_OK;
}
catch ( ... )
{
hRes = E_OUTOFMEMORY;
}
return hRes;
}
HRESULT Term() throw()
{
DeleteCriticalSection(&m_sec);
return S_OK;
}
private:
CRITICAL_SECTION m_sec;
};
class CAutoCriticalSection : public CCriticalSection
{
public:
CAutoCriticalSection()
: m_bTermed(true)
{
HRESULT hr = CCriticalSection::Init();
if ( FAILED(hr) )
{
throw hr;
}
m_bTermed = false;
}
~CAutoCriticalSection()throw()
{
try
{
Term();
}
catch ( ... )
{
}
}
HRESULT Term()throw()
{
HRESULT hRes = S_OK;
if ( !m_bTermed )
{
m_bTermed = true;
hRes = CCriticalSection::Term();
}
return hRes;
}
private:
HRESULT Init();
private:
bool m_bTermed;
};
class CSafeDeleteCriticalSection : public CCriticalSection
{
public:
CSafeDeleteCriticalSection()
: m_bInitialized(false)
{
}
~CSafeDeleteCriticalSection()throw()
{
try
{
Term();//Derived class
}
catch ( ... )
{
//log
}
}
HRESULT Init()throw()
{
HRESULT hr = CCriticalSection::Init();
if ( SUCCEEDED(hr) )
{
m_bInitialized = true;
}
return hr;
}
HRESULT Term()throw()
{
HRESULT hRes = S_OK;
if ( m_bInitialized )
{
m_bInitialized = false;
hRes = CCriticalSection::Term();
}
return hRes;
}
HRESULT Lock()throw()
{
HRESULT hRes = S_OK;
if ( m_bInitialized )
{
hRes = CCriticalSection::Lock();
}
return hRes;
}
HRESULT Unlock()throw()
{
HRESULT hRes = S_OK;
if ( m_bInitialized )
{
hRes = CCriticalSection::Unlock();
}
return hRes;
}
private:
bool m_bInitialized;
};
class CFakeCriticalSection
{
public:
HRESULT Lock() throw() { return S_OK; }
HRESULT Unlock() throw() { return S_OK; }
HRESULT Init() throw() { return S_OK; }
HRESULT Term() throw() { return S_OK; }
};
class CMultiThreadModel
{
public:
typedef CAutoCriticalSection AutoDeleteCriticalSection;
};
class CSingleThreadModel
{
public:
typedef CFakeCriticalSection AutoDeleteCriticalSection;
};
//foward declaration
template < typename ThreadModel >
class CObjectLockT;
template < typename ThreadModel >
class CObjectRootEx
{
public:
typedef typename ThreadModel::AutoDeleteCriticalSection _AutoDelCritSec;
typedef CObjectLockT<ThreadModel> ObjectLock;
public:
void Lock()
{
m_critsec.Lock();
}
void Unlock()
{
m_critsec.Unlock();
}
private:
_AutoDelCritSec m_critsec;
};
template < typename ThreadModel >
class CObjectLockT
{
public:
CObjectLockT(CObjectRootEx<ThreadModel>* p)
: m_p(p)
, m_bUnlocked(false)
{
if ( m_p )
{
m_p->Lock();
}
}
~CObjectLockT()
{
try
{
Term();
}
catch ( ... )
{
//Prevent exception leak out from deconstruction
}
}
void Term()throw()
{
if ( !m_bUnlocked )
{
if ( m_p )
{
m_p->Unlock();
}
m_bUnlocked = true;
}
}
private:
CObjectLockT(const CObjectLockT&);
CObjectLockT& operator==(const CObjectLockT&);//
private:
CObjectRootEx<ThreadModel>* m_p;
bool m_bUnlocked;
};
#endif
//
///以下为测试代码
#include <iostream>
#include <windows.h>
#include "asyn.h"
using namespace std;
///
///Test Code
//
class CTestClass
: public CObjectRootEx < CSingleThreadModel > //Test not asynchronous
//:public CObjectRootEx < CMultiThreadModel > //Test asynchronous
{
public:
void TestFunc();
};
void CTestClass::TestFunc()
{
ObjectLock clObjectLock(this); //Lock
cout << "CTestClass::TestFunc come in" << endl;
///Other code
///Your data does't be visited by multithread at the same time.
Sleep(3000);//Test Lock
cout << "Sleep over" << endl;
}
CTestClass clCTestClass;
DWORD ThreadProc(
LPVOID lpParameter
)
{
clCTestClass.TestFunc(); //Test Thread visit
return 0;
}
void main()
{
DWORD dwThreadID = 0;
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadProc, NULL, 0, &dwThreadID);
clCTestClass.TestFunc();//Main Thread visit
Sleep(5000);
}