可以參考這篇博文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
}