您现在的位置:中国下载站学院中心网络编程Visual C++教程Visual C++入门教程 → 文章列表

MFC教程(9)-- MFC的进程和线程(2)

作者:佚名  来源:不详  发布时间:2007-3-27 17:34:36   

减小字体 增大字体

 
 

  下面以一个动态链接到MFC DLL的单模块应用程序为例,说明这些对象的创建过程。

  当第一次访问状态信息时,比如使用 AfxGetModuleState得到模块状态,导致系列创建过程的开始,如图9-7所示。

  首先分析语句pState=_afxThreadState。如果_afxThreadData、线程状态和模块状态还没有创建,该语句可以导致这些数据的创建。

  pState声明为CNoTrackObject对象的指针,_afxThreadState声明为一个模板CThreadLocal的实例,pState=_afxThreadData为什么可以通过编译器的检查呢?因为CThreadLocal模板重载了操作符“”*”和“->”,这两个运算返回CNoTrackObject类型的对象。回顾3.2节CThreadLocalObject、CThreadLocal的定义,这两个操作符运算到最后都是调用CThreadLocalObject的成员函数GetData。

  创建_afxThreadData所指对象和线程状态

  CThreadLocalObject::GetData用来获取线程局部变量(这个例子中是线程状态)的值,其参数用来创建动态的线程局部变量。图9-7的上面的虚线框表示其流程:

  它检查成员变量m_nSlot是否等于0(线程局部变量是否曾经被分配了MFC线程私有空间槽位),检查全局变量_afxTheadData指针是否为空。如果_afxThreadData空,则创建一个CThreadSlotData类对象,让_afxThreadData指向它,这样本程序的MFC线程局部存储的管理者被创建。如果m_nSlot等于0,则让_afxThreadDtata调用AllocSlot分配一个槽位并把槽号保存在m_nSlot中。

  得到了线程局部变量(线程状态)所占用的槽位后,委托_afxThreadData调用GetThreadValue(m_nSlot)得到线程状态值(指针)。如果结果非空,则返回它;如果结果是NULL,则表明该线程状态还没有被创建,于是使用参数创建一个动态的线程状态,并使用SetValue把其指针保存在槽m_nSlot中,返回该指针。

  创建模块状态

[责任编辑:cndownzcom]

  得到了线程状态的值后,通过它得到模块状态m_pModuleState。如果m_pModuleState为空,表明该线程状态是才创建的,其许多成员变量还没有赋值,程序的进程模块状态还没有被创建。于是调用函数_afxBaseModule.GetData,导致进程模块状态被创建。

  图9-7的下面一个虚线框表示了CProcessLocalObject::GetData的创建过程:

  _afxBaseModule首先检查成员变量m_pObject是否空,如果非空就返回它,即进程模块状态指针;否则,在堆中创建一个动态的_AFX_BASE_MODULE_STATE对象,返回。

  从上述两个GetData的实现可以看出,CThreadLocal模板对象负责线程局部变量的创建和管理(查询,修改,删除);CProcessLocal模板对象负责进程局部变量的创建和管理(查询,修改,删除)。

  模块-线程状态的创建

  模块状态的成员模块-线程状态m_thread的创建类似于线程状态的创建:当第一次访问m_thread所对应的CThreadLocal模板对象时,给m_thread分配MFC线程局部存储的私有槽号m_nSlot,并动态地创建_AFX_MODULE_THREAD_STATE对象,保存对象指针在m_nSlot槽中。

  创建过程所涉及的几个重要函数的算法

  创建过程所涉及的几个重要函数的算法描述如下:

  AllocSlot

  AllocSlot用来分配线程的MFC私有存储空间的槽号。由于该函数要修改全局变量_afxThreadData,所以必须使用m_sect关键段对象来同步多个线程对该函数的调用。

  CThreadSlotData::AllocSlot()

  {

  进入关键段代码(EnterCriticalSection(m_sect);)

  搜索m_pSlotData,查找空槽(SLOT)

  如果不存在空槽(第一次进入时,肯定不存在)

  分配或再分配内存以创建新槽,

  指针m_pSlotData指向分配的地址。

  得到新槽(SLOT)

  标志该SLOT为已用

  记录最新可用的SLOT到成员变量m_nRover中。

  离开关键段代码(LeaveCriticalSection(m_sect);)

  返回槽号

  }

  GetThreadValue

  GetThreadValue用来获取调用线程的第slot个线程局部变量的值。每一个线程局部变量都占用一个且只一个槽位。

  CThreadSlotData::GetThreadValue(int slot)

  {

  //得到一个CThreadData型的指针pData

  //pData指向MFC线程私有存储空间

  //m_tlsIndex在_afxThreadData创建时由构造函数创建

  pData=(CThreadData*)TlsGetValue(m_tlsIndex),。

  如果指针空或slot>pData->nCount, 则返回空。

[责任编辑:cndownzcom]

  否则,返回pData

  }

  SetValue

  SetValue用来把调用线程的第slot个线程局部变量的值(指针)存放到线程的MFC私有存储空间的第slot个槽位。

  CThreadSlotData::SetValue(int slot, void *pValue)

  {

  //通过TLS索引得到线程的MFC私有存储空间

  pData = (CThreadData*)TlsGetValue(m_tlsIndex)

  //没有得到值或者pValue非空且当前槽号,即

  //线程局部变量的个数

  //大于使用当前局部变量的线程个数时

  if (pData NULL or slot > pData->nCount && pValue!=NULL)

  {

  if pData NULL //当前线程第一次访问该线程局部变量

  {

  创建一个CThreadData实例;

  添加到CThreadSlotData::m_list;

  令pData指向它;

  }

  按目前为止,线程局部变量的个数为pData->pData分配或重分配内存,

  用来容纳指向真正线程数据的指针

  调用TlsSetValue(pData)保存pData

  }

  //把指向真正线程数据的pValue保存在pData对应的slot中

  pData->pData[slot] = pValue

  }

  管理状态

  在描述了MFC状态的实现机制之后,现在来讨论MFC的状态管理和相关状态的作用。

  模块状态切换

  模块状态切换就是把当前线程的线程状态的m_pModuleState指针指向即将运行模块模块状态。

  MFC使用AFX_MANAGE_STATE宏来完成模块状态的切换,即进入模块时使用当前模板模板状态,并保存原模板状态;退出模块时恢复原来的模块状态。这相当于状态的压栈和出栈。实现原理如下。

  先看MFC关于AFX_MANAGE_STATE的定义:

  #ifdef _AFXDLL

  struct AFX_MAINTAIN_STATE

  {

  AFX_MAINTAIN_STATE(AFX_MODULE_STATE* pModuleState);

  ~AFX_MAINTAIN_STATE();

  protected:

  AFX_MODULE_STATE* m_pPrevModuleState;

  };

  //AFX_MANAGE_STATE宏的定义:

  #define AFX_MANAGE_STATE(p) AFX_MAINTAIN_STATE _ctlState(p);

  #else // _AFXDLL

  #define AFX_MANAGE_STATE(p)

  #endif //!_AFXDLL

  如果使用MFC DLL,MFC提供类AFX_MAINTAIN_STATE来实现状态的压栈和出栈,AFX_MANAGE_SATATE宏的作用是定义一个AFX_MAINTAIN_STATE类型的局部变量_ctlState。

  AFX_MAINTAIN_STATE的构造函数在其成员变量m_pPrevModuleState中保存当前的模块状态对象,并把参数指定的模块状态设定为当前模块状态。所以该宏作为入口点的第一条语句就切换了模块状态。

[责任编辑:cndownzcom]

  在退出模块时,局部变量_ctlState将自动地销毁,这导致AFX_MAINTAIN_STATE的析构函数被调用,析构函数把保存在m_pPrevModuleState的状态设置为当前状态。

  AFX_MANAGE_SATATE的参数在不同场合是不一样的,例如,

  DLL的输出函数使用

  AFX_MANAGE_SATATE(AfxGetStaticModuleState());

  其中,AfxGetStaticModuleState返回DLL的模块状态afxModuleState。

  窗口函数使用

  AFX_MANAGE_STATE(_afxBaseModuleState.GetData());

  其中,_afxBaseModuleState.GetData()返回的是应用程序的全局模块状态。

  OLE使用的模块切换方法有所不同,这里不作讨论。

  上面讨论了线程执行行不同模块的代码时切换模块状态的情况。在线程创建时怎么处理模块状态呢?

  一个进程(使用MFC的应用程序)的主线程创建线程模块状态和进程模块状态,前者是_AFX_THREAD_STATE类的实例,后者是_AFX_BASE_MODULE_STATE类的实例。

  当进程的新的线程被创建时,它创建自己的线程状态,继承父线程的模块状态。在线程的入口函数_AfxThreadEntry完成这样的处理,该函数的描述见8.5.3节。

  扩展DLL的模块状态

  7.3.1节指出扩展DLL的实现必须遵循五条规则,为此,首先在扩展DLL实现文件里头,定义AFX_EXTENSION_MODULE类型的静态扩展模块变量,然后在DllMain入口函数里头使用AfxInitExtension初始化扩展模块变量,并且实现和输出一个初始化函数供扩展DLL的使用者调用。

  使用者必须具备一个CWinApp对象,通常在它的InitInstance函数中调用扩展DLL提供的初始化函数。

  一般用以下的几段代码完成上述任务。首先是扩展模块变量的定义和初始化:

  static AFX_EXTENSION_MODULE extensionDLL;

  DllMain(HINSTANCE hInstance, DWORD dwReason


在百度中搜索更多MFC教程(9)-- MFC的进程和线程(2)相关网页 转贴于:中国下载站

  • 上一篇文章:MFC教程(10)-- 内存分配方式和调试机制
  • 下一篇文章:MFC教程(9)-- MFC的进程和线程(1)
  • 阅读统计:[]
  • 中国下载站】【设为主页】【收藏本页】【打印本文】【回到顶部】【关闭此页

    相关文章
    文章评论(评论内容只代表网友观点,与本站立场无关!)

    用户名: 查看更多评论

    分 值:100分 85分 70分 55分 40分 25分 10分 0分

    内 容:

             (注“”为必填内容。) 验证码: 验证码,看不清楚?请点击刷新验证码


    设为首页 - 关于我们 - 广告服务 - 网站地图 - 加入收藏 - 网站声明 - 网站帮助 - 友情链接

    • Copyright (C) 2006-2008 www.cndownz.com All Rights Reserved.
      中国下载站 版权所有. 粤ICP备05141802号. 对本站有任何建议、意见或投诉,请来信:cndownzcom@yahoo.com.cn.
      喜欢中国下载站(cndownz.com),请把中国下载站(cndownz.com)告诉你QQ上的5位好友,多谢支持!