微客户/服务器体系结构
MiniGUI-Threads 运行模式基于多线程机制,采用了微客户/服务器体系结构,如下图所示:
 |
| MiniGUI-Thread 体系结构 |
MiniGUI 内部有几个重要的线程,它们分别完成不同的系统任务:
- Dekstop 线程用于管理所有窗口,包括建立、销毁、显示、隐藏、修改 Z 序
节点、获得输入焦点等。它是 MiniGUI-Threads 的核心微服务器线程,当应用
线程需要创建、销毁窗口时,都要向 Desktop 发送请求;
- Event 线程用来从 IAL 中收集鼠标和键盘事件,并将收集到的事件转换成消
息并邮寄给 desktop 微服务器;
- Timer 线程用来触发定时器事件。该线程以“休眠-运行-休眠”的方式运行,
运行时向 Desktop 服务器发送定时器消息,之后立即进入休眠状态。 Desktop
接收到定时器消息时,会查看当前窗口的定时器列表,如果某个定时器过期,
则向该定时器所属的窗口发送定时器消息。
微客户/服务器机制的核心实现主要集中在消息队列数据结构上,如 MiniGUI 中的 desktop 微服务器管理窗口的创建和销毁。当一个线程要求 desktop 服务器建立一个窗口时,该线程首先在 desktop 的消息队列中放置一条消息,然后进入休眠状态等待 desktop 处理这一消息。当desktop 处理完当前任务后,或正处于休眠时,它就处理这个消息。消息处理完成时,desktop 唤醒等待的线程,并返回一个处理结果。下一节将详细介绍消息队列。
消息队列
MiniGUI 处理如下几类消息:
- 邮寄消息,由 PostMessage 消息发送到消息队列后立即返回,若消息队列中的邮寄消息缓冲
区已慢,则该函数返回错误值;
- 同步消息,由 PostSyncMessage 发送,用来向不同于调用该函数的线程消息
队列邮寄消息,并且只有该消息被处理之后,该函数才返回;
- 通知消息,通过 SendNotifyMessage 发送,将消息放入消息队列后立即返回;
- 异步消息,通过 SendAsyncMessage 发送,系统直接调用目标窗口的窗口过
程。
MiniGUI 采用了下图所示的消息队列
 |
| 消息队列示意图 |
上面提到,MiniGUI 的消息有邮寄消息、同步消息、通知消息等多种类型,
MiniGUI 为它们分别建立了消息队列,同时通过一个消息队列状态字来记录目前
各个队列中是否有消息。
状态字通过标志位表示如下状态:
- 是否有邮寄消息;
- 是否有通知消息
- 是否有同步消息
- 是否有退出消息
- 是否有重绘消息
- 是否有定时器消息
从队列的实现看,通知消息队列和同步消息队列使用链表实现,邮寄消息用环形
队列实现。
入队和出队
MiniGUI 通过不同的接口实现不同类型的消息的入队:
- PostMessage,发送邮寄消息,消息入邮寄消息队列;
- PostSyncMessage,发送同步消息,消息如同步消息队列;
- SendMessage,可以向任意一个窗口发送消息,消息处理完后该函数返回,若
目标窗口所在线程和调用线程是一个线程,则该函数直接调用窗口过程;
- SendNotifyMessage,发送通知消息,消息如通知消息队列;
- SendAsyncMessage,发送异步消息,系统直接调用目标窗口的窗口过程。
应用程序通过消息循环对消息队列进行处理,如下所示:
while (GetMessage (&Msg, hMainWnd))
dispatchMessage ();
处理一条消息分两步:
- 一是从消息队列中取出一条消息;
- 二是调用对应的窗口过程函数处理这条消息。
由于不同种类消息具有不同的特性,同时,一些消息比较特殊,比如 PAINT 重
绘消息通常比较费时,所以从队列中取消息是按一定的优先顺序完成的,如下图所示:
 |
| 读消息流程图 |
从先到后 MiniGUI 依次处理 QUIT 消息,SYNMSG 消息,NOTIFYMSG 消息,普通
消息,Paint 重绘消息和 timer 消息,消息的种类根据队列状态字来确定。
处理消息实际上就是调用应用程序的窗口过程函数和系统默认的窗口过程函数来
对消息进行处理。
需要注意的问题
关于消息队列还有一些问题需要注意:
- 很多消息带有附加数据,如鼠标按下消息带有按键和点击位置这样的数据,
需要处理好;
- 需要提供多种消息传递机制,如在某些情况下,发送消息的窗口要等到整个
消息处理完才能继续执行,而有些情况下,窗口发出消息后就继续往下执行,
还有更复杂的情况,如发送消息时设置一个超时值,以处理消息得不到及时
处理的情况;
- 某些特殊消息也需要注意,如定时器,当定时器的频率很高,而窗口的反应
速度很慢时,消息队列可能会塞满。再比如,窗口重绘消息,通常比较费时,
只有当程序将其它消息处理完后,才处理重绘消息。
参考资源
|