前言
原本这篇博客的标题叫《窗体和线程漫谈》,但想来想去确实不太合适。由于我确实没有写关于窗体和线程的不论什么理论知识,而仅仅是探讨了工作线程怎样将数据的处理结果显示到窗体这个问题,因此又一次改动标题。
另外,关于窗体和线程的相关理论知识。感觉一两句话确实说不清楚,并且《Windows 核心编程》这本书上介绍的也挺好的。有机会再写吧。特别是感觉如今好多人都直接在学 MFC,用 MFC。甚至连窗体过程,消息循环都不太明确,假设能有这样一篇博客也是非常有价值的。
为什么要讨论这个问题
讨论这个依旧是跟之前的项目经历有关。
这里暂且称该项目为 A 项目。
A 项目包含一个client和服务端,client有一个核心的网络模块。该网络模块基于完毕port开发,有多个工作线程。网络模块负责接收来自服务端的数据,对这些数据进行处理,并将终于的处理结果显示在窗体上。那么问题来了,工作线程怎样将数据的终于处理结果显示在窗体上?能够说这个问题不仅仅是 A 项目中遇到的问题。绝大多数网络应用程序都会遇到这个问题。当时 A 项目中採用的方案是在工作线程中利用窗体句柄直接调用对应函数(如 SendMessage)对窗体进行操作。
尽管这样的做法当时并没有出现故障?但它真的没问题吗?假设有问题,我们该採用什么方式将终于的处理结果显示在窗体上呢?
窗体和线程的关系
理论知识是我们回答上述问题的基础。
这方面我找到的唯一资料就是 《Windows 核心编程》 第 26 章 窗体消息。
对这块不太明确的兄弟。能够先看下。这里我们就不详述了。
在工作线程中利用窗体句柄直接调用对应函数对窗体进行操作这样的做法有没有问题
绝大多数情况下。确实不会出现故障。但仅仅是绝大多数情况下。以下是出现故障的两种情况。
- 这样的情况是我在项目 A 中亲身经历的。
当时我在工作线程中调用了 SetFocus 这个函数,结果这个函数并没有成功返回,为什么?以下是 SetFocus 文档中的描写叙述。
Sets the keyboard focus to the specified window. The window must be attached to the calling thread’s message queue.
原因非常明显。传递给 SetFocus 函数的窗体句柄代表的窗体必须是属于调用 SetFocus 函数的这个线程的。但工作线程并没拥有该窗体,也没有拥有不论什么窗体。那为什么调用 SetFoucs 函数要有这个需求呢?这个仅仅能说我也不太清楚,但结合 《Windows 核心编程》 第 26 章 中的相关描写叙述。每个线程都拥有自己的键盘焦点,应该在一定程度上回答了这个问题。
- 这样的情况是《多线程编程中的主界面安全处理》这篇文章中提到的,大致情况就是拥有窗体的线程正在等待工作线程退出。而工作线程正堵塞于对窗体的 SendMessage 调用中。应该说,这样的情况不是没有可能发生。
说了这么多,说究竟我是不建议在工作线程中利用窗体句柄直接调用相关函数对窗体进程操作这样的做法的,尽管这样的做法绝大多数情况下不会出错,但一旦出现故障,排查起来就比較困难。但我也不全然否认这样的做法,前提是,程序猿自身一定要对程序本身的逻辑认识清楚,比方在工作线程中会不会调用相似 SetFocus 的这类函数,会不会出现上面提到的另外一种情况。
工作线程怎样将终于的处理结果显示到窗体
既然,在工作线程中通过窗体句柄直接调用相关函数对窗体进行操作的这样的做法不太好,那採用什么方法将终于的处理结果显示到窗体上呢?我的方法是调用 PostMessage,通过自己定义消息将数据交给窗体的窗体过程处理。也就是拥有窗体的线程处理。这样的情况下工作线程中唯一和窗体相关的操作就是通过窗体句柄调用 PostMessage。眼下,我并想不到这样的做法有什么有问题的地方,假设有人认为有问题,大家能够一起讨论下。
另外,假设有兄弟知道更好的做法,大家也能够讨论下。
感受和思考
在查找线程和窗体的相关资料过程中。发现非常多人都在问关于线程和窗体的一些问题,在讨论关于窗体和线程的一些概念,并且搞出来一些非常玄乎的东西。但事实上搞清楚一些基础知识,自己独立思考一下。这些问题并不难回答。或许 MFC 相对传统的 Win32 API 确实方便些。但窗体过程。消息循环等一些基本概念还是要理解的。