《Description of CWnd derived MFC objects and multithreaded applications in Visual C++》译文

原文地址:
http://support.microsoft.com/default.aspx?scid=kb;en-us;147578

译文:
《Visual C++ MFC CWnd及其子类多线程编程说明》

概要:
用MFC开发多线程程序,不应该跨线程传递MFC对象。常规的做法是,一个线程只访问在该线程内创建的MFC对象。如果不这样做,可能导致运行时问题,比如引发断言,或者是一些非期望的程序行为。


详细内容:
一个Win32进程里,所有在这个进程空间里运行的线程都可以访问所有的全局数据和静态数据。一个线程可以使用thread-local-storage(TLS:线程区域存储空间)(译者注1)存储该线程专用的数据。

在一个多线程环境里,由于窗口是属于某个线程的,因此MFC在TLS里维护着一个临时性的和一个永久性的窗口句柄map。除了窗口之外,其它像GDI对象、DC等等,也是有一样的句柄map。在TLS里维护窗口句柄map可以有效避免多个线程同时访问的问题(译者注2)。

基于以上事实,CHandleMap::LookupPermanent(HANDLE h)和CHandleMap::LookupTemporary(HANDLE h)这两个函数就应运而生了。这两个函数入参是一个句柄,然后这两个函数分别在当前线程里的永久性和临时性的句柄map里找出该句柄所关联的MFC对象。这就意味着,假如在其它线程里用CWnd类创建了一个窗口,那么在本线程以该窗口的句柄作为入参去调用这两个函数,就会找不到对应的CWnd对象了。

以下是用到CHandleMap::LookupPermanent(HANDLE h)和CHandleMap::LookupTemporary(HANDLE h)的函数举例:CWnd::AssertValid()(因此,CWnd对象的ASSERT_VALID宏也一样用到了)。这个函数会对对象的合法性作检查。如果AssertValid()里,m_hWnd成员在两个map里面都找不到对应的MFC对象,又或者找出了不合法的对象(译者注3)的话,那么就会触发断言。在VC++2.1里,这些断言在Wincore.cpp的797行和798行;在VC++2.2里,那些断言在Wincore.cpp的804行和805行;在VC++4.0里,在Wincore.cpp的871行和872行。

在MFC源码里,ASSERT_VALID宏的调用是随处可见的。因此,对于某个线程,如果你直接或间接调用了其它线程的MFC对象的ASSERT_VALID的话,你就会触发断言;假如你没有触发断言,你的程序仍然会发生一些异常行为,因为你不被允许直接操作由别的线程创建的MFC窗口对象。

解决以上问题正确的做法是使用窗口句柄,而不使用MFC对象。跨线程传递窗口句柄是安全的。如果线程A传递一个窗口句柄到线程B,那么线程B可以对这个窗口句柄进行SendMessage()或者PostMessage()。当这些消息被处理完之后,程序会返回到线程A去执行CWnd::AssertValid()检查,并成功地从句柄map里找到该句柄对应的MFC窗口对象。

在上述的这种情况里,线程B也可以调用CWnd::FromHandle()去获取该句柄的一个临时CWnd对象,并且该对象会存储在线程B的临时句柄map里。但尽管如此,这个对象可能也只能提供一些有限的操作,因为没有任何途径可以使线程B的这个CWnd对象可以同步到线程A的句柄map里去的。

 


注1:有关“thread local storage”(TLS),可以google“thread local storage”获取更详细的信息;也可以参阅《Windows程序设计》“多任务和多线程”一章里的“线程区域储存空间 (TLS)”一节。
注2:避免同时访问什么呢?译者重复看原文看了不下30遍,也找不出来。
注3:关于判断“不合法的对象”,译者认为是AssertValid()里的这一句——“ASSERT((CWnd*)p == this);   // must be us”判断的。举个例子,在线程B里使用线程A创建的CWnd,由于线程B里的CWnd::AssertValid()找不到m_hWnd对应的CWnd,所以触发断言——就在这个场景下,译者有一次为了避开这个断言,在线程B里写了一句类似“CWnd *pWndB = CWnd::FromHandle(pWndA->m_hWnd);”的代码,企图在线程B的句柄map里生成一个以线程A CWnd句柄的“句柄与对象”关联关系。当译者沾沾自喜地再次调试程序时,发现这次却断在“ASSERT((CWnd*)p == this);”这一句里了。原来,AssertValid()发现在线程B句柄map里的对象和线程A的对象根本不是同一个对象,那么这也是不合法的。这AssertValid()果然够严谨。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值