在分析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;
}

_AFX_THREAD_STATE  就是MFC关于线程状态的类  声明在afxstat_.h中,而_afxThreadState 是一个全局模板对象位于定义在afxstate.cpp中,先来看下这个类的声明:

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);
#endifvirtual ~CNoTrackObject() {};
};
class _AFX_THREAD_STATE : public CNoTrackObject    //虚基类
{
public:_AFX_THREAD_STATE();virtual ~_AFX_THREAD_STATE();// override for m_pModuleState in _AFX_APP_STATEAFX_MODULE_STATE* m_pModuleState;            //****  AFX_MODULE_STATE 重要的一个类,保存了当前线程执行代码所在模块的信息。 ****//AFX_MODULE_STATE* m_pPrevModuleState;// memory safety pool for temp mapsvoid* m_pSafetyPoolBuffer;    // current buffer// thread local exception contextAFX_EXCEPTION_CONTEXT m_exceptionContext;// CWnd create, gray dialog hook, and other hook dataCWnd* m_pWndInit;   //CWND类在创建每个窗口前 用这个指针标记要被HOOK的窗口所对应的类的指针,参见 AfxHookWindowCreateCWnd* m_pAlternateWndInit;      // special case commdlg hooking     通用对话框HOOK的标记指针DWORD m_dwPropStyle;DWORD m_dwPropExStyle;          //属性页切换类型保存HWND m_hWndInit;HHOOK m_hHookOldCbtFilter;          //保存CBT钩子函数的句柄,_AfxCbtFilterHook 在窗口创建后钩子函数将窗口过程设置为AfxWndProcHHOOK m_hHookOldMsgFilter;// message pump for RunMSG m_msgCur;                   // current messageCPoint m_ptCursorLast;          // last mouse positionUINT m_nMsgLast;                // last mouse message  //三个用于CWinApp的消息循环#ifdef _DEBUGint m_nDisablePumpCount; // Diagnostic trap to detect illegal re-entrancy
#endif// other CWnd modal dataMSG m_lastSentMsg;              // see CWnd::WindowProcHWND m_hTrackingWindow;         // see CWnd::TrackPopupMenuHMENU m_hTrackingMenu;TCHAR m_szTempClassName[_AFX_TEMP_CLASS_NAME_SIZE];    // see AfxRegisterWndClassHWND m_hLockoutNotifyWindow;    // see CWnd::OnCommandBOOL m_bInMsgFilter;                                         //以上为每个线程的MFC窗口类的消息路由提供支持和// other framework modal dataCView* m_pRoutingView;          // see CCmdTarget::GetRoutingViewCPushRoutingView* m_pPushRoutingView;CFrameWnd* m_pRoutingFrame;     // see CCmdTarget::GetRoutingFrameCPushRoutingFrame* m_pPushRoutingFrame;// MFC/DB thread-local dataBOOL m_bWaitForDataSource;// OLE control thread-local dataCWnd* m_pWndPark;       // "parking space" windowlong m_nCtrlRef;        // reference count on parking windowBOOL m_bNeedTerm;       // TRUE if OleUninitialize needs to be called
};

这个类确切的说应该更像是个结构体,并没有给类定义任何功能性的接口函数,主要用来为MFC的消息路由机制提供全局变量支持,其中两个AFX_MODULE_STATE指针 在编写MFC的DLL文件的时候很重要。

回过头来看 _afxThreadState 这个全局对象,它在afxstate.cpp 中是这样定义的

THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)

THREAD_LOCAL宏为

#define THREAD_LOCAL(class_name, ident_name) \AFX_COMDAT CThreadLocal<class_name> ident_name;

这里_afxThreadState  是一个CThreadLocal<_AFX_THREAD_STATE> 模板类的实例对象。

template<class TYPE>    //声明在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:
// AttributesCNoTrackObject* GetData(CNoTrackObject* (AFXAPI* pfnCreateObject)());CNoTrackObject* GetDataNA();// Implementationint m_nSlot;  ~CThreadLocalObject();
};

在调用AfxGetThreadState()中

_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<CNoTrackObject*>(_afxThreadData->GetThreadValue(m_nSlot));if (pValue == NULL)  //CThreadSlotData对象以线程本地存储机制来存放数据,同样的槽号每个线程中对应不同的数据指针{// allocate zero-init objectpValue = (*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 CSimpleListint nCount;         // current size of pDataLPVOID* 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();// Operationsint AllocSlot();void FreeSlot(int nSlot); void SetValue(int nSlot, void* pValue);// delete all values in process/threadvoid DeleteValues(HINSTANCE hInst, BOOL bAll = FALSE);// assign instance handle to just constructed slotsvoid AssignInstance(HINSTANCE hInst);// ImplementationDWORD 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 slotsint 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 structuresCRITICAL_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 beginningfor (nSlot = 1;nSlot < nAlloc && (m_pSlotData[nSlot].dwFlags & SLOT_USED); nSlot++);// if none found, need to allocate more spaceif (nSlot >= nAlloc){// realloc memory for the bit array and the slot memoryint nNewAlloc = m_nAlloc+32;  //分配粒度32HGLOBAL hSlotData;if (m_pSlotData == NULL){hSlotData = GlobalAlloc(GMEM_MOVEABLE, static_cast<UINT>(::ATL::AtlMultiplyThrow(static_cast<UINT>(nNewAlloc),static_cast<UINT>(sizeof(CSlotData)))));
                                                                                     //nNewAlloc * 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);}if (hSlotData == NULL){if (m_pSlotData != NULL)GlobalLock(GlobalHandle(m_pSlotData));LeaveCriticalSection(&m_sect);AfxThrowMemoryException();}CSlotData* pSlotData = (CSlotData*)GlobalLock(hSlotData);// always zero initialize after successmemset(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 allocatedif (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 yetif (pData == NULL)   {TRY{pData = new CThreadData;}CATCH_ALL(e){LeaveCriticalSection(&m_sect);THROW_LAST();}END_CATCH_ALLpData->nCount = 0;pData->pData = NULL;DEBUG_ONLY(pData->pNext = NULL);m_list.AddHead(pData);}// grow to now current sizevoid** ppvTemp;if (pData->pData == NULL)   //为CThreadData 中的pDaData指针数组分配内存,增加槽号就增加内存的分配ppvTemp = (void**)LocalAlloc(LMEM_FIXED, static_cast<UINT>(::ATL::AtlMultiplyThrow(static_cast<UINT>(m_nMax),static_cast<UINT>(sizeof(LPVOID)))));elseppvTemp = (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();}pData->pData = ppvTemp;// initialize the newly allocated partmemset(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 <iostream>
#include <string>
#include <afxwin.h>class DiyString:public CNoTrackObject,public std::string
{
public:DiyString(){}
};CThreadLocal<DiyString>  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<char *> (lpParameter));std::cout<<*pStr<<std::endl;return 0;
}int _tmain(int argc, _TCHAR* argv[])
{for(int i=0; i<10; ++i){char date[256] = {0};sprintf_s(date,"This is %d thread been created",i);::CreateThread(NULL,NULL,ThreadProc,date,NULL,NULL);Sleep(100);  //延迟确保date中数据被创建的线程保存到了ThreadLocal}system("pause");return 0;
}

运行结果如下

这套机制在编写多线程程序的时候是很好用的,但是也有些缺点,

第一 必须包含 afxwin.h 设置使用MFC库,使用SDK的程序想要摘出来使用必须自己去修改 这个四个文件中的一些依赖

第一 没有在线程退出的时候释放资源的接口 ,用来释放已经在堆中申请的对象

AfxGetThreadState 与 _AFX_THREAD_STATE 剖析相关推荐

  1. 读书笔记 来自网络

    2010年3月15日 # <深入解析MFC>笔记 12. 进程与线程 2009-10-7 ======================= <深入解析MFC>笔记 12. 进程与 ...

  2. MFC程序的剖析及生死因果揭秘

    本文剖析MFC底层的程序脉络,主要是由MFC深入浅出的学习笔记而得. SDK下Windows程序的编程:对于一般SDK下Windows程序的编程,其主要步骤如下: 各步骤说明: 1:WinMain函数 ...

  3. volatile关键字之全面深度剖析

    引言 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字 ...

  4. TensorFlow基础剖析

    TensorFlow基础剖析 一.概述 TensorFlow 是一个使用数据流图 (Dataflow Graph) 表达数值计算的开源软件库.它使用节点表示抽象的数学计算,并使用 OP 表达计算的逻辑 ...

  5. c语言赋值x为字母,C语言算术、赋值、关系、逻辑运算详细剖析---

    标识符和关键字 ¨标识符:用来标识程序中的变量.符号常量.函数.数组.类型.文件等对象的名字.标识符只能由字母.数字和下划线组成,且第一个字符必需为字母或下划线.C语言中大小写字母是两个不同的字符. ...

  6. 如何在HHDI中进行数据质量探查并获取数据剖析报告

    通过执行多种数据剖析规则,对目标表(或一段SQL语句)进行数据质量探查,从而得到其数据质量情况.目前支持以下几种数据剖析类型,分别是:数字值分析.值匹配检查.字符值分析.日期值分析.布尔值分析.重复值 ...

  7. 老李推荐:第14章4节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-端口转发 1...

    老李推荐:第14章4节<MonkeyRunner源码剖析> HierarchyViewer实现原理-装备ViewServer-端口转发 在初始化HierarchyViewer的实例过程中, ...

  8. JS魔法堂:mmDeferred源码剖析

    一.前言 avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见 ...

  9. Linux 文件系统剖析

    Linux 文件系统剖析 按照分层结构讨论 Linux 文件系统 M. Tim Jones, 顾问工程师, Emulex Corp. 简介: 在文件系统方面,Linux® 可以算得上操作系统中的 &q ...

最新文章

  1. C#精髓【月儿原创】第一讲 使用垃圾回收器
  2. Java中Filter、Servlet、Listener的学习
  3. pl/sql中三种游标循环效率对比
  4. 词性标注,实体识别,ICTCLAS分析系统的学习
  5. java多线程模型_1、java线程模型
  6. 1.9 Java转换流:InputStreamReader和OutputStreamWriter
  7. jQuery的Password Validation插件
  8. python svn模块_使用Python实现一键批量更新SVN/Git模块的脚本
  9. 图像语义分割:U-Net网络和PSP网络
  10. mysql的单页应用框架搭建_采用vue+webpack构建的单页应用——私人博客MintloG诞生记...
  11. 一壶 100℃ 的开水从多高倒进嘴里不会觉得烫?
  12. OpenCV学习笔记(三):多通道图像分离、混合算子:split(),merge()
  13. 生日快乐程序员的浪漫代码_python告白代码,只属于程序员的浪漫
  14. 高老师架构设计思考短句集(1)
  15. python之str与bytes互转
  16. 【正点原子FPGA连载】第三十九章OV7725摄像头RGB-LCD显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1
  17. 【CV】Reg2Net:一种用于计算机视觉任务的多尺度骨干架构
  18. 批量选择图片_PS的批量处理功能
  19. R语言中的函数5:purrrmap()
  20. 下了个蓝屏代码查看工具,就中病毒了。。。什么鬼病毒,竟然还是用的VBS

热门文章

  1. Ubuntu18右上角输入法图标突然消失,而且无法输入中文
  2. JAVA工具类(17)--Java导入导出Excel工具类ExcelUtil
  3. 完美世界如何修改服务器ip,完美世界:完美整容及修改身材流程
  4. matlab中的箭头符号怎么打开,MATLAB中上下标、斜体、箭头等符号的使用方法
  5. 正则表达式提取html内容
  6. 推荐一款牛逼的Windows神器!功能很强大!
  7. 魏德米勒端子eplan宏_Eplan部件库和宏全集
  8. Set 直接转成 数组
  9. Hystrix 使用
  10. open /data/prometheus: too many open files