我是靠谱客的博主 伶俐自行车,最近开发中收集的这篇文章主要介绍MFC线程独立对象管理机制分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

可以参考这篇博文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有一个。它用在哪呢,用在全局唯一的那个_afxThreadDatam_pSlotDatam_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线程独立对象管理机制分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(69)

评论列表共有 0 条评论

立即
投稿
返回
顶部