在分析MFC的源码中经常看到 AfxGetThreadState这个函数,它用来返回当前线程的一些信息,其代码主要在 afxstat_.h afxtls_.h 及它们对应的 afxstate.cpp 和afxstl.cpp中,AfxGetTheradState函数的定义位于 afxstate.cpp中,定义如下:
_AFX_THREAD_STATE* AFXAPI AfxGetThreadState()
{
_AFX_THREAD_STATE *pState =_afxThreadState.GetData(); //_afxThreadState是一个全局模板类对象
ENSURE(pState != NULL);
return pState;
}
class AFX_NOVTABLE CNoTrackObject
{
public:
void* PASCAL operator new(size_t nSize);
void PASCAL operator delete(void*);
#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
void* PASCAL operator new(size_t nSize, LPCSTR, int);
void PASCAL operator delete(void* pObject, LPCSTR, int);
#endif
virtual ~CNoTrackObject() {};
};
class _AFX_THREAD_STATE : public CNoTrackObject //虚基类
{
public:
_AFX_THREAD_STATE();
virtual ~_AFX_THREAD_STATE();
// override for m_pModuleState in _AFX_APP_STATE
AFX_MODULE_STATE* m_pModuleState; //**** AFX_MODULE_STATE 重要的一个类,保存了当前线程执行代码所在模块的信息。 ****//
AFX_MODULE_STATE* m_pPrevModuleState;
// memory safety pool for temp maps
void* m_pSafetyPoolBuffer; // current buffer
// thread local exception context
AFX_EXCEPTION_CONTEXT m_exceptionContext;
// CWnd create, gray dialog hook, and other hook data
CWnd* m_pWndInit; //CWND类在创建每个窗口前 用这个指针标记要被HOOK的窗口所对应的类的指针,参见 AfxHookWindowCreate
CWnd* m_pAlternateWndInit; // special case commdlg hooking 通用对话框HOOK的标记指针
DWORD m_dwPropStyle;
DWORD m_dwPropExStyle; //属性页切换类型保存
HWND m_hWndInit;
HHOOK m_hHookOldCbtFilter; //保存CBT钩子函数的句柄,_AfxCbtFilterHook 在窗口创建后钩子函数将窗口过程设置为AfxWndProc
HHOOK m_hHookOldMsgFilter;
// message pump for Run
MSG m_msgCur; // current message
CPoint m_ptCursorLast; // last mouse position
UINT m_nMsgLast; // last mouse message //三个用于CWinApp的消息循环
#ifdef _DEBUG
int m_nDisablePumpCount; // Diagnostic trap to detect illegal re-entrancy
#endif
// other CWnd modal data
MSG m_lastSentMsg; // see CWnd::WindowProc
HWND m_hTrackingWindow; // see CWnd::TrackPopupMenu
HMENU m_hTrackingMenu;
TCHAR m_szTempClassName[_AFX_TEMP_CLASS_NAME_SIZE]; // see AfxRegisterWndClass
HWND m_hLockoutNotifyWindow; // see CWnd::OnCommand
BOOL m_bInMsgFilter; //以上为每个线程的MFC窗口类的消息路由提供支持和
// other framework modal data
CView* m_pRoutingView; // see CCmdTarget::GetRoutingView
CPushRoutingView* m_pPushRoutingView;
CFrameWnd* m_pRoutingFrame; // see CCmdTarget::GetRoutingFrame
CPushRoutingFrame* m_pPushRoutingFrame;
// MFC/DB thread-local data
BOOL m_bWaitForDataSource;
// OLE control thread-local data
CWnd* m_pWndPark; // "parking space" window
long m_nCtrlRef; // reference count on parking window
BOOL m_bNeedTerm; // TRUE if OleUninitialize needs to be called
};
回过头来看 _afxThreadState 这个全局对象,它在afxstate.cpp 中是这样定义的
THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
THREAD_LOCAL宏为
#define THREAD_LOCAL(class_name, ident_name) \
AFX_COMDAT CThreadLocal ident_name;
这里_afxThreadState 是一个CThreadLocal<_AFX_THREAD_STATE> 模板类的实例对象。
template //声明在afxstl_.h文件中
class CThreadLocal : public CThreadLocalObject
{
// Attributes
public:
AFX_INLINE TYPE* GetData()
{
TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject);
ENSURE(pData != NULL);
return pData;
}
AFX_INLINE TYPE* GetDataNA()
{
TYPE* pData = (TYPE*)CThreadLocalObject::GetDataNA();
return pData;
}
AFX_INLINE operator TYPE*()
{
return GetData();
}
AFX_INLINE TYPE* operator->()
{
return GetData();
}
// Implementation
public:
static CNoTrackObject* AFXAPI CreateObject()
{ return new TYPE; }
这个模板类只是对其基类 CThreadLocalObject 的简单扩展,再看CThreadLocalObject 的声明,
class AFX_NOVTABLE CThreadLocalObject
{
public:
// Attributes
CNoTrackObject* GetData(CNoTrackObject* (AFXAPI* pfnCreateObject)());
CNoTrackObject* GetDataNA();
// Implementation
int m_nSlot;
~CThreadLocalObject();
};
_AFX_THREAD_STATE *pState =_afxThreadState.GetData(); //这个CThreadLocal<_AFX_THREAD_STATE> 类型的全局对象调用了CThreadLocalObject的GetData函数,这个函数
的实现如下:
CNoTrackObject* CThreadLocalObject::GetData(
CNoTrackObject* (AFXAPI* pfnCreateObject)())
{
ENSURE(pfnCreateObject);
if (m_nSlot == 0)
{
if (_afxThreadData == NULL) //全局指针变量 指向 CThreadSlotData 类型的对象 在第一次调用的时候 在堆中创建对象赋给指针
{
_afxThreadData = new(__afxThreadData) CThreadSlotData; //
ENSURE(_afxThreadData != NULL);
}
m_nSlot = _afxThreadData->AllocSlot(); //返回申请到的槽号,
ENSURE(m_nSlot != 0);
}
CNoTrackObject* pValue = static_cast(_afxThreadData->GetThreadValue(m_nSlot));
if (pValue == NULL) //CThreadSlotData对象以线程本地存储机制来存放数据,同样的槽号每个线程中对应不同的数据指针
{
// allocate zero-init object
pValue = (*pfnCreateObject)(); //如果m_nSolt槽号获取到的数据指针无效,在堆中创建一个AFX_THREAD_STATE对象,pfnCreateObject函数指针对应
NEW AFX_THREAD_STATE
// set tls data to newly created object
_afxThreadData->SetValue(m_nSlot, pValue); //将创建的AFX_THREAD_STATE对象用线程局部储存,保存。
ASSERT(_afxThreadData->GetThreadValue(m_nSlot) == pValue);
}
return pValue;
_afxThreadData是一个全局对象指针,__afxThreadData (注意多了一个下划线)则是一个全局数组,
_afxThreadData = new(__afxThreadData) CThreadSlotData; // 在全局静态空间中创建了一个CThreadSlotData 对象
他们的定义在afxtls.cpp中:
// global _afxThreadData used to allocate thread local indexes
BYTE __afxThreadData[sizeof(CThreadSlotData)];
CThreadSlotData* _afxThreadData;
CThreadSlotData 使用了线程本地储存的机制来存放每个线程的 AFX_THREAD_STATE 对象,来看下它的声明和实现
struct CThreadData : public CNoTrackObject
{
CThreadData* pNext; // required to be member of CSimpleList
int nCount; // current size of pData
LPVOID* pData; // actual thread local data (indexed by nSlot)
};
struct CSlotData
{
DWORD dwFlags; // slot flags (allocated/not allocated)
HINSTANCE hInst; // module which owns this slot
};
class CThreadSlotData
{
public:
CThreadSlotData();
// Operations
int AllocSlot();
void FreeSlot(int nSlot);
void SetValue(int nSlot, void* pValue);
// delete all values in process/thread
void DeleteValues(HINSTANCE hInst, BOOL bAll = FALSE);
// assign instance handle to just constructed slots
void AssignInstance(HINSTANCE hInst);
// Implementation
DWORD m_tlsIndex; // used to access system thread-local storage 线程本地储存的索引号
int m_nAlloc; // number of slots allocated (in UINTs)
int m_nRover; // (optimization) for quick finding of free slots
int m_nMax; // size of slot table below (in bits)
CSlotData* m_pSlotData; // state of each slot (allocated or not)
CTypedSimpleList m_list; // list of CThreadData structures
CRITICAL_SECTION m_sect;
void* GetThreadValue(int nSlot); // special version for threads only!
void* PASCAL operator new(size_t, void* p)
{ return p; }
void DeleteValues(CThreadData* pData, HINSTANCE hInst);
~CThreadSlotData();
int CThreadSlotData::AllocSlot()
{
EnterCriticalSection(&m_sect);
int nAlloc = m_nAlloc;
int nSlot = m_nRover;
if (nSlot >= nAlloc || (m_pSlotData[nSlot].dwFlags & SLOT_USED)) //如果没有给m_pSlotData分配过 或者分配的内存不够的话
{
// search for first free slot, starting at beginning
for (nSlot = 1;
nSlot < nAlloc && (m_pSlotData[nSlot].dwFlags & SLOT_USED); nSlot++)
;
// if none found, need to allocate more space
if (nSlot >= nAlloc)
{
// realloc memory for the bit array and the slot memory
int nNewAlloc = m_nAlloc+32; //分配粒度32
HGLOBAL hSlotData;
if (m_pSlotData == NULL)
{
hSlotData = GlobalAlloc(GMEM_MOVEABLE, static_cast(::ATL::AtlMultiplyThrow(static_cast(nNewAlloc),static_cast(sizeof(CSlotData)))));
//nNewAlloc * sizeof(CSlotData)
}
else
{
hSlotData = GlobalHandle(m_pSlotData);
GlobalUnlock(hSlotData);
hSlotData = GlobalReAlloc(hSlotData, static_cast(::ATL::AtlMultiplyThrow(static_cast(nNewAlloc),static_cast(sizeof(CSlotData)))), GMEM_MOVEABLE|GMEM_SHARE);
}
if (hSlotData == NULL)
{
if (m_pSlotData != NULL)
GlobalLock(GlobalHandle(m_pSlotData));
LeaveCriticalSection(&m_sect);
AfxThrowMemoryException();
}
CSlotData* pSlotData = (CSlotData*)GlobalLock(hSlotData);
// always zero initialize after success
memset(pSlotData+m_nAlloc, 0, (nNewAlloc-m_nAlloc)*sizeof(CSlotData));
m_nAlloc = nNewAlloc;
m_pSlotData = pSlotData;
}
}
ASSERT(nSlot != 0); // first slot (0) is reserved
// adjust m_nMax to largest slot ever allocated
if (nSlot >= m_nMax)
m_nMax = nSlot+1;
ASSERT(!(m_pSlotData[nSlot].dwFlags & SLOT_USED));
m_pSlotData[nSlot].dwFlags |= SLOT_USED; //将槽号为nSolt的CSlotData的标志设置为已使用
// update m_nRover (likely place to find a free slot is next one)
m_nRover = nSlot+1;
LeaveCriticalSection(&m_sect);
return nSlot; // slot can be used for FreeSlot, GetValue, SetValue
}
void CThreadSlotData::SetValue(int nSlot, void* pValue)
{
EnterCriticalSection(&m_sect); //临界区对象 防止多线程重入
ASSERT(nSlot != 0 && nSlot < m_nMax);
ASSERT(m_pSlotData != NULL);
ASSERT(m_pSlotData[nSlot].dwFlags & SLOT_USED);
if( nSlot <= 0 || nSlot >= m_nMax ) // check for retail builds.
{
LeaveCriticalSection(&m_sect);
return;
}
CThreadData* pData = (CThreadData*)TlsGetValue(m_tlsIndex); //使用线程本地储存 保证每个线程拥有自己的CThreadDate 结构在堆中
if (pData == NULL || nSlot >= pData->nCount && pValue != NULL)
{
// if pData is NULL then this thread has not been visited yet
if (pData == NULL)
{
TRY
{
pData = new CThreadData;
}
CATCH_ALL(e)
{
LeaveCriticalSection(&m_sect);
THROW_LAST();
}
END_CATCH_ALL
pData->nCount = 0;
pData->pData = NULL;
DEBUG_ONLY(pData->pNext = NULL);
m_list.AddHead(pData);
}
// grow to now current size
void** ppvTemp;
if (pData->pData == NULL) //为CThreadData 中的pDaData指针数组分配内存,增加槽号就增加内存的分配
ppvTemp = (void**)LocalAlloc(LMEM_FIXED, static_cast(::ATL::AtlMultiplyThrow(static_cast(m_nMax),static_cast(sizeof(LPVOID)))));
else
ppvTemp = (void**)LocalReAlloc(pData->pData, static_cast(::ATL::AtlMultiplyThrow(static_cast(m_nMax),static_cast(sizeof(LPVOID)))), LMEM_MOVEABLE);
if (ppvTemp == NULL)
{
LeaveCriticalSection(&m_sect);
AfxThrowMemoryException();
}
pData->pData = ppvTemp;
// initialize the newly allocated part
memset(pData->pData + pData->nCount, 0,
(m_nMax - pData->nCount) * sizeof(LPVOID));
pData->nCount = m_nMax;
TlsSetValue(m_tlsIndex, pData);
}
ASSERT(pData->pData != NULL && nSlot < pData->nCount);
if( pData->pData != NULL && nSlot < pData->nCount )
pData->pData[nSlot] = pValue; //根据槽号设置CThreadData 中的pDaData(LPVOID类型,对应属于每个线程的需要保存的对象的
指针)指针数组中指针指向的对象
LeaveCriticalSection(&m_sect);
}
inline void* CThreadSlotData::GetThreadValue(int nSlot)
{
EnterCriticalSection(&m_sect);
ASSERT(nSlot != 0 && nSlot < m_nMax);
ASSERT(m_pSlotData != NULL);
ASSERT(m_pSlotData[nSlot].dwFlags & SLOT_USED);
ASSERT(m_tlsIndex != (DWORD)-1);
if( nSlot <= 0 || nSlot >= m_nMax ) // check for retail builds. 槽号小于等于0 或者大于已存在的槽号 无意义
{
LeaveCriticalSection(&m_sect);
return NULL;
}
CThreadData* pData = (CThreadData*)TlsGetValue(m_tlsIndex); //获取属于本线程的CThreadData对象指针
if (pData == NULL || nSlot >= pData->nCount)
{
LeaveCriticalSection(&m_sect);
return NULL;
}
void* pRetVal = pData->pData[nSlot]; //根据槽号返回pData指针数组中对应的对象指针
LeaveCriticalSection(&m_sect);
return pRetVal;
}
通过上面的代码可以看到CThreadLocal<_AFX_THREAD_STATE> 类型的全局对象_afxThreadState , 事实上是封装了对CThreadSlotData类型的全局指_afxThreadSlotDate
的使用,所有的工作由_afxThreadSlotDate (CThreadSlotData类对象的指针)来完成。这个类用本地线程储存实现了每个线程拥有自己的_AFX_THREAD_STATE对象。
这套机制实现起来看似很复杂,但却是相当有用的,在MFC的实现中会发现N多的调用。
如过我们自己在编写多线程程序的时候,想要为每个线程保存一个属于本线程的对象的时候可以方便的使用这个机制。比如我们想要每个线程用一个独立的DiyString对象来保存属于该线程的一些字符信息 我们可以定义一个全局CThreadLocal< DiyString>对象 g_ThreadString然后为它定义一个全局的适配函数 ,注意,利用这套机制来保存的线程对象必须继承自 CNoTrackObject,并且有默认构造函数
#include "stdafx.h"
#include
#include
#include
class DiyString:public CNoTrackObject,public std::string
{
public:
DiyString()
{
}
};
CThreadLocal g_ThreadString;
DiyString * g_GetThreadString()
{
DiyString * ret =0;
ret = g_ThreadString.GetData();
return ret;
}
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
DiyString * pStr = g_GetThreadString();
//DiyString * pStr = g_ThreadString; //这样也可以 因为CThreadLocal模板类重载了 类型转换操作符
pStr->append(static_cast (lpParameter));
std::cout<<*pStr<
运行结果如下
这套机制在编写多线程程序的时候是很好用的,但是也有些缺点,
第一 必须包含 afxwin.h 设置使用MFC库,使用SDK的程序想要摘出来使用必须自己去修改 这个四个文件中的一些依赖
第一 没有在线程退出的时候释放资源的接口 ,用来释放已经在堆中申请的对象