VC多线程--在线程之间传递窗口句柄是安全的

2024-06-08 03:08

本文主要是介绍VC多线程--在线程之间传递窗口句柄是安全的,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

总结:使用MFC编写多线程应用程序,不能跨线程传递MFC对象。

解决的方法是使用窗口句柄而不是MFC对象。在线程之间传递窗口句柄是安全的。如果线程A向线程B传递一个窗口句柄,那么,线程B可以通过发送消息给拥有该句柄的窗口对象。在处理窗口消息时,系统已经切换到线程A。这是验证窗口对象的有效性会成功。


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窗口对象映射中的对象同步。


这篇关于VC多线程--在线程之间传递窗口句柄是安全的的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1041057

相关文章

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2

Java多线程父线程向子线程传值问题及解决

《Java多线程父线程向子线程传值问题及解决》文章总结了5种解决父子之间数据传递困扰的解决方案,包括ThreadLocal+TaskDecorator、UserUtils、CustomTaskDeco... 目录1 背景2 ThreadLocal+TaskDecorator3 RequestContextH

java父子线程之间实现共享传递数据

《java父子线程之间实现共享传递数据》本文介绍了Java中父子线程间共享传递数据的几种方法,包括ThreadLocal变量、并发集合和内存队列或消息队列,并提醒注意并发安全问题... 目录通过 ThreadLocal 变量共享数据通过并发集合共享数据通过内存队列或消息队列共享数据注意并发安全问题总结在 J

Java文件与Base64之间的转化方式

《Java文件与Base64之间的转化方式》这篇文章介绍了如何使用Java将文件(如图片、视频)转换为Base64编码,以及如何将Base64编码转换回文件,通过提供具体的工具类实现,作者希望帮助读者... 目录Java文件与Base64之间的转化1、文件转Base64工具类2、Base64转文件工具类3、

异步线程traceId如何实现传递

《异步线程traceId如何实现传递》文章介绍了如何在异步请求中传递traceId,通过重写ThreadPoolTaskExecutor的方法和实现TaskDecorator接口来增强线程池,确保异步... 目录前言重写ThreadPoolTaskExecutor中方法线程池增强总结前言在日常问题排查中,

C#多线程编程中导致死锁的常见陷阱和避免方法

《C#多线程编程中导致死锁的常见陷阱和避免方法》在C#多线程编程中,死锁(Deadlock)是一种常见的、令人头疼的错误,死锁通常发生在多个线程试图获取多个资源的锁时,导致相互等待对方释放资源,最终形... 目录引言1. 什么是死锁?死锁的典型条件:2. 导致死锁的常见原因2.1 锁的顺序问题错误示例:不同

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

Python调用另一个py文件并传递参数常见的方法及其应用场景

《Python调用另一个py文件并传递参数常见的方法及其应用场景》:本文主要介绍在Python中调用另一个py文件并传递参数的几种常见方法,包括使用import语句、exec函数、subproce... 目录前言1. 使用import语句1.1 基本用法1.2 导入特定函数1.3 处理文件路径2. 使用ex

Java向kettle8.0传递参数的方式总结

《Java向kettle8.0传递参数的方式总结》介绍了如何在Kettle中传递参数到转换和作业中,包括设置全局properties、使用TransMeta和JobMeta的parameterValu... 目录1.传递参数到转换中2.传递参数到作业中总结1.传递参数到转换中1.1. 通过设置Trans的

bat脚本启动git bash窗口,并执行命令方式

《bat脚本启动gitbash窗口,并执行命令方式》本文介绍了如何在Windows服务器上使用cmd启动jar包时出现乱码的问题,并提供了解决方法——使用GitBash窗口启动并设置编码,通过编写s... 目录一、简介二、使用说明2.1 start.BAT脚本2.2 参数说明2.3 效果总结一、简介某些情