译文:VC++中CWnd及其子类和多线程应用程序的描述..
时间:2010-03-07 来源:flyfish520
Description of CWnd derived MFC objects and multithreaded applications in Visual C++
http://support.microsoft.com/default.aspx?scid=kb;en-us;147578
VC++中CWnd及其子类和多线程应用程序的描述
SUMMARY
In a multi-threaded application written using MFC, you should not pass MFC objects across thread boundaries. As a general rule, a thread should access only those MFC objects that it creates. Failure to do so may cause run-time problems including assertions or unexpected program behavior.
使用MFC编写多线程应用程序,不能跨线程传递MFC对象。作为一种基本的准则,线程只允许操作由其本身创建的MFC对象。不遵守该准则将导致断言(assertion)或者无法预知的程序行为等运行期错误。
MORE INFORMATION
In a Win32 process, all the threads running in the process address space can view all global and static data. A thread can use thread-local-storage (TLS) to store any thread-specific data.
所有运行在win32进程地址空间中的线程能够查看全局和静态的数据。线程使用“线程局部存储(TLS)”技术来存储跟线程相关的数据。
In a multi-threaded environment because windows are owned by threads, MFC keeps the temporary and permanent window handle map in thread local storage. The same is true for other handle maps like those for GDI objects and device contexts. Keeping the window handle maps in thread local storage ensures protection against simultaneous access by several threads.
在多线程环境中,由于所有的窗口元素都通过线程来管理,于是MFC将暂时/永久窗口<->句柄映射保存在TLS中。其他的窗口句柄映射以及设备描述表也是采用类似的方法存储。将这些窗口句柄映射存储在TLS中能够防止其他线程同时访问这些数据。
The behavior of the functions CHandleMap::LookupPermanent() and CHandleMap::LookupTemporary() is a direct consequence of these facts. Given a window handle, these functions check the permanent and temporary window handle maps of the current thread for the existence of an associated CWnd derived MFC object. This means that if calls to these functions are made from a thread to search for MFC objects that represent windows created in other threads, these calls will fail.
函数:CHandleMap::LookupPermanent() 和 CHandleMap::LookupTemporary()就是这种理论的直接产物。给定窗口句柄,这些函数通过查找临时以及永久的窗口句柄映射来获得CWnd或者其子类对象。这就意味着如果一个线程调用这些函数来查找其他线程的CWnd或者其子类对象,调用将失败。
There are several functions that call CHandleMap::LookupPermanent() and CHandleMap::LookupTemporary(). CWnd::AssertValid() (and hence the macro ASSERT_VALID for a CWnd object) is one such function. This function is called to make validity checks on an object. If AssertValid() fails to find an entry for the MFC object's m_hWnd member in any of the handle maps or finds an incorrect entry, it fires an assertion. In Visual C++ 2.1, these assertions are in file Wincore.cpp, lines 797 and 798. In Visual C++ 2.2, they are in Wincore.cpp, lines 804 and 805. In Visual C++ 4.0, they are in Wincore.cpp, lines 871 and 872.
MFC中有大量函数调用CHandleMap::LookupPermanent() and CHandleMap::LookupTemporary()。CWnd::AssertValid()就是其中一个(宏ASSERT_VALID也是一个).调用该函数来检查对象的有效性。如果没有发现有效的CWnd对象,则会报“断言(Assertion)”错误。VC2.1中,该错误出现在wincore.cpp, lines 797 & 798, VC2.2中,出现在wincore.cpp行804和805,VC4.0则出现在行871和872。
Calls to the ASSERT_VALID macro are sprinkled all over the MFC source code. Hence, from a particular thread, if you end up calling a function that calls ASSERT_VALID on MFC window objects that belong to some other thread, you get an assertion. If you do not get an assertion, you may still get abnormal behavior because you are not allowed to directly manipulate windows created by other threads.
宏ASSERT_VALID的调用在MFC中相当频繁。这样,如果你调用该宏来验证其他线程对象的有效性,就会出现断言错误。就算不出现断言错误,也会导致程序异常退出,这是因为不允许直接操作其他线程中的CWnd对象。
The correct approach in such situations is to work with window handles, not MFC objects. It is safe to pass window handles across thread boundaries. If thread A passes a window handle to thread B, then thread B can use this window handle to send or post messages to the window. When these messages are processed, you are back in the context of thread A and calls to CWnd::AssertValid() to check thread A's window handle maps will succeed.
解决的方法是使用窗口句柄而不是MFC对象。在线程之间传递窗口句柄是安全的。如果线程A向线程B传递一个窗口句柄,那么,线程B可以通过发送消息给拥有该句柄的窗口对象。在处理窗口消息时,系统已经切换到线程A。这是验证窗口对象的有效性会成功。
In this scenario, thread B can also use the CWnd::FromHandle() function to get a temporary CWnd object which is placed in thread B's temporary handle map. However this object may be of only limited use, because in no way is it in synchronization with the MFC object existing in thread A's handle maps.
与此同时,线程B能够调用CWnd::FromHandle()函数来获得暂时的窗口对象,不过这一对象应该谨慎使用。因为它无法与存在于线程A窗口对象映射中的对象同步。