概述
可以参考这篇博文https://blog.csdn.net/likePeak/article/details/3595982
#define EXTERN_THREAD_LOCAL(class_name, ident_name)
extern CThreadLocal<class_name> ident_name;
THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
声明了一个全局变量CThreadLocal<_AFX_THREAD_STATE> _afxThreadState;
#define PROCESS_LOCAL(class_name, ident_name)
AFX_COMDAT CProcessLocal<class_name> ident_name;
PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)
定义了一个全局变量CProcessLocal<_AFX_BASE_MODULE_STATE> _afxBaseModuleState;
// global _afxThreadData used to allocate thread local indexes
BYTE __afxThreadData[sizeof(CThreadSlotData)];
CThreadSlotData* _afxThreadData;
// AFX_MODULE_THREAD_STATE (local to thread *and* module)
class AFX_MODULE_THREAD_STATE : public CNoTrackObject
{
}
// AFX_MODULE_STATE (global data for a module)
class AFX_MODULE_STATE : public CNoTrackObject
{
}
class _AFX_THREAD_STATE : public CNoTrackObject
{
}
#ifdef _AFXDLL
#define _AFX_CMDTARGET_GETSTATE() (m_pModuleState)
#else
#define _AFX_CMDTARGET_GETSTATE() (AfxGetModuleState())
#endif
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)
};
_afxThreadData这个全局变量用来实现存取获取所有mfc线程的线程局部数据的功能。
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<CThreadData*> 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();
};
TlsAlloc()分配一个m_tlsIndex用来给所有线程分配TLS数据。_afxThreadData是MFC全局唯一的,那么m_tlsIndex也是全局唯一的。m_list这里面每一个CThreadData就代表一个MFC线程的数据。
假若我们加入THREAD_LOCAL(TStruct, threadState3)产生一个全局变量:CThreadLocal<TStruct> threadState3;这个threadState3是全局变量,那么它的构造函数也应该是在主线程里调用的,但是产生TStruct的构造函数是在某个线程里第一次调用threadState3->a时调用的。
struct TStruct:public CNoTrackObject
{
int a;
TStruct()
{
a = 111;;
}
};
DWORD __stdcall pfnThread2(LPVOID)
{
int iA3 = threadState3->a;
return 0;
}
threadState3->a会调用下面CThreadLocal的这个函数。这里的TYPE其实就是TStruct。
AFX_INLINE TYPE* operator->()
{
return GetData();
}
继续往下走,到CThreadLocal的下面这个函数
AFX_INLINE TYPE* GetData()
{
TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject);
ENSURE(pData != NULL);
return pData;
}
static CNoTrackObject* AFXAPI CreateObject()
{ return new TYPE; }
继续走到CThreadLocalObject::GetData
CNoTrackObject* CThreadLocalObject::GetData(
CNoTrackObject* (AFXAPI* pfnCreateObject)())
{
if (m_nSlot == 0)
{
if (_afxThreadData == NULL)//全局的这玩意儿第一调用时new一下下
{
_afxThreadData = new(__afxThreadData) CThreadSlotData;
}
m_nSlot = _afxThreadData->AllocSlot();//找一个还未使用的slot号
}
//下面这个GetThreadValue就是根据m_nSlot找到相应的PVOID
CNoTrackObject* pValue = static_cast<CNoTrackObject*>(_afxThreadData->GetThreadValue(m_nSlot));
if (pValue == NULL)//如果是空就存放一下
{
// allocate zero-init object
pValue = (*pfnCreateObject)();//这个就是 new TYPE;
// set tls data to newly created object
_afxThreadData->SetValue(m_nSlot, pValue);//把这个pValue 即那个PVOID存到相应槽
}
return pValue;
}
m_nSlot这个玩意一个线程或者说一个CThreadLocal有一个。它用在哪呢,用在全局唯一的那个_afxThreadData的m_pSlotData,m_list以及一个PVOID数组,这个数组内存是由LocalAlloc分配的,这个数组全局的,供所有线程使用。
// special version of CThreadSlotData::GetData that only works with
// thread local storage (and not process local storage)
// this version is inlined and simplified for speed
inline void* CThreadSlotData::GetThreadValue(int nSlot)
{
EnterCriticalSection(&m_sect);
if( nSlot <= 0 || nSlot >= m_nMax ) // check for retail builds.
{
LeaveCriticalSection(&m_sect);
return NULL;
}
//第一次调用的时候,返回NULL
CThreadData* pData = (CThreadData*)TlsGetValue(m_tlsIndex);
if (pData == NULL || nSlot >= pData->nCount)
{
LeaveCriticalSection(&m_sect);
return NULL;
}
//CThreadData* pData不为空,就能返回pData->pData[nSlot],因为TlsSetValue的时候,设 //置这个void*,即我们的TYPE*
void* pRetVal = pData->pData[nSlot];
//LeaveCriticalSection(&m_sect);
return pRetVal;
}
如果上面这个函数返回NULL,说明此线程是第一次调用GetData,就要调用下面这个函数了。
void CThreadSlotData::SetValue(int nSlot, void* pValue)
{
EnterCriticalSection(&m_sect);
if( nSlot <= 0 || nSlot >= m_nMax ) // check for retail builds.
{
LeaveCriticalSection(&m_sect);
return;
}
CThreadData* pData = (CThreadData*)TlsGetValue(m_tlsIndex);
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);//将我们线程的CThreadData放入全局list
}
// grow to now current size
//每一个线程的CThreadData的pData 全部指向ppvTemp,即这个全局局部堆。然后每一 //个CThreadData再根据nSlot从这个堆找到自己线程的那个真正数据TYPE*指向那玩意, //即我们创建的TStruct对象
void** ppvTemp;
if (pData->pData == NULL)
ppvTemp = (void**)LocalAlloc(LMEM_FIXED, static_cast<UINT>(::ATL::AtlMultiplyThrow(static_cast<UINT>(m_nMax),static_cast<UINT>(sizeof(LPVOID)))));
else
ppvTemp = (void**)LocalReAlloc(pData->pData, static_cast<UINT>(::ATL::AtlMultiplyThrow(static_cast<UINT>(m_nMax),static_cast<UINT>(sizeof(LPVOID)))), LMEM_MOVEABLE);
if (ppvTemp == NULL)
{
LeaveCriticalSection(&m_sect);
AfxThrowMemoryException();
}
//全指向这个LPVOID数组的第一个元素
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传递的是CThreadData*参数,CThreadData里面放着我们自己的线程数据
TlsSetValue(m_tlsIndex, pData);
}
if( pData->pData != NULL && nSlot < pData->nCount )
pData->pData[nSlot] = pValue;//虽然pData->pData指向局部堆第一个LPVOID,但是利 //用nSlot在ppvTemp 相应位置存放我们自己的线程数据
LeaveCriticalSection(&m_sect);
}
HLOCAL LocalAlloc(
__in UINT uFlags,
__in SIZE_T uBytes
);
上面用到的LocalAlloc看着很咋呼,其实API很简单不是。就是分配m_nMax个LPVOID。
下面看一下分配slot的代码:
初始化状态为:
m_nAlloc = 0;
m_nRover = 1; // first slot (0) is always reserved
m_nMax = 0;
int CThreadSlotData::AllocSlot()
{
EnterCriticalSection(&m_sect);
int nAlloc = m_nAlloc;
int nSlot = m_nRover;
if (nSlot >= nAlloc || (m_pSlotData[nSlot].dwFlags & SLOT_USED))
{
// 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,m_nAlloc相当于vector里的容量
HGLOBAL hSlotData;
if (m_pSlotData == NULL)
{
hSlotData = GlobalAlloc(GMEM_MOVEABLE, static_cast<UINT>(::ATL::AtlMultiplyThrow(static_cast<UINT>(nNewAlloc),static_cast<UINT>(sizeof(CSlotData)))));
}
else
{
hSlotData = GlobalHandle(m_pSlotData);
GlobalUnlock(hSlotData);
hSlotData = GlobalReAlloc(hSlotData, static_cast<UINT>(::ATL::AtlMultiplyThrow(static_cast<UINT>(nNewAlloc),static_cast<UINT>(sizeof(CSlotData)))), GMEM_MOVEABLE|GMEM_SHARE);
}
//pSlotData的数量与m_nAlloc是相等的,下面这句代码就是把分配内存赋值给pSlotData
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;
}
}
// adjust m_nMax to largest slot ever allocated
//m_nMax比曾经分配的最大的nSlot要大1,可能是因为第0号slot是不用的,所以加1
if (nSlot >= m_nMax)
m_nMax = nSlot+1;
ASSERT(!(m_pSlotData[nSlot].dwFlags & SLOT_USED));
m_pSlotData[nSlot].dwFlags |= SLOT_USED;
// 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
}
最后
以上就是伶俐自行车为你收集整理的MFC线程独立对象管理机制分析的全部内容,希望文章能够帮你解决MFC线程独立对象管理机制分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复