可以参考这篇博文https://blog.csdn.net/likePeak/article/details/3595982
1
2
3#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;
1
2
3#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;
1
2
3// global _afxThreadData used to allocate thread local indexes BYTE __afxThreadData[sizeof(CThreadSlotData)]; CThreadSlotData* _afxThreadData;
1
2
3
4
5
6
7
8
9
10
11// 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 { }
1
2
3
4
5#ifdef _AFXDLL #define _AFX_CMDTARGET_GETSTATE() (m_pModuleState) #else #define _AFX_CMDTARGET_GETSTATE() (AfxGetModuleState()) #endif
1
2
3
4
5
6struct 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线程的线程局部数据的功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30class 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时调用的。
1
2
3
4
5
6
7
8
9
10
11
12
13struct TStruct:public CNoTrackObject { int a; TStruct() { a = 111;; } }; DWORD __stdcall pfnThread2(LPVOID) { int iA3 = threadState3->a; return 0; }
threadState3->a会调用下面CThreadLocal的这个函数。这里的TYPE其实就是TStruct。
1
2
3
4AFX_INLINE TYPE* operator->() { return GetData(); }
继续往下走,到CThreadLocal的下面这个函数
1
2
3
4
5
6
7
8AFX_INLINE TYPE* GetData() { TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject); ENSURE(pData != NULL); return pData; } static CNoTrackObject* AFXAPI CreateObject() { return new TYPE; }
继续走到CThreadLocalObject::GetData
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22CNoTrackObject* 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分配的,这个数组全局的,供所有线程使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 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,就要调用下面这个函数了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56void 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); }
1
2
3
4HLOCAL LocalAlloc( __in UINT uFlags, __in SIZE_T uBytes );
上面用到的LocalAlloc看着很咋呼,其实API很简单不是。就是分配m_nMax个LPVOID。
下面看一下分配slot的代码:
初始化状态为:
1
2
3m_nAlloc = 0; m_nRover = 1; // first slot (0) is always reserved m_nMax = 0;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50int 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线程独立对象管理机制分析内容请搜索靠谱客的其他文章。
发表评论 取消回复