Windows中的Work线程和GUI线程
Windows线程分为两种:Worker线程、GUI线程
worker线程:是指完全不牵扯到图形用户界面(GUI),纯粹做运算的线程。
GUI线程:负责建造窗口以及处理消息循环(拥有消息队列)。任何一个特定窗口的消息总是被产生这一窗口的线程抓到并处理(线程收到消息后派发给不同的窗口函数处理)。
操作系统会监控计算机上的键盘和鼠标等输入设备,为每一个输入事件(由用户操作所引发,比如用户按了某个键)生成一个消息。根据事件发生时的情况(比如当前激活的窗体负责接收用户按键,而依据用户点击鼠标的坐标可以知道用户在哪个窗体区域内点击了鼠标),操作系统会确定出此消息应该发给哪个窗体对象。这些生成的消息会统一地先临时放置在一个“系统消息队列(system message queue)”中,然后,操作系统有一个专门的线程负责从这一队列中取出消息,根据消息的目标对象(就是窗体的句柄),将其移动到创建它的UI线程所对应的消息队列中。(操作系统在创建进程和线程时,都同时记录了大量的控制信息,比如通过进程控制块和句柄表可以查找到进程所创建的所有线程和引用的核心对象,因此,根据窗体句柄来确定此消息应属于哪个UI线程对于操作系统来说是很简单的一件事)。UI线程启动一个消息循环,每次从本线程所对应的消息队列中取出一条消息,然后根据消息所包容的信息,将其转发给特定的窗体对象,此窗体对象所对应的“窗体过程”函数被调用以处理这些消息。
在Win32中,每一个GUI线程有它自己专属的消息队列。(这并不意味着每一个窗口有它自己的消息队列,因为一个线程可以产生许多窗口。如果一个线程停止回应,或是它忙于一段耗时的计算工作,那么由它产生的窗口统统都会停止回应,但系统中的其他窗口还会继续正常工作。)
以下是一个非常基本的规则,用来管理Win32中的线程、消息、窗口的互动: 所有传送给某一窗口之消息,将由产生该窗口之线程负责处理。 比方说,使用SetWindowText来更新一个Edit框的内容,其实就是发出了一个WM_SETTEXT消息给edit窗口函数。推而广之,每一个控件都是一个窗口,都拥有自己的窗口函数。 对窗口所作的一切事情基本上都会被该窗口的窗口函数处理,并因此被产生该窗口的线程处理。
当需要发送一个消息时,Windows会自动计算出哪一个线程应该接收到消息(以便确定该消息实体应该挂在在哪一个线程的消息队列中)。同时,windows还会确定线程应该如何被告知有这么一个消息进来。一共有四种可能:
(1)如果属于同一线程,使用SendMessage传递消息,则直接调用窗口函数。
(2)如果属于同一线程,使用PostMessage传递消息,则把消息放在消息队列中然后立即返回。 (3)如果不属于同一线程,使用SendMessage传递消息,则切换到新线程中并调用窗口函数。在该窗口函数结束之前,SendMessage不会返回。
(4)PostMessage立刻返回,消息则被放到另一线程的消息队列中。
当我send一个消息给另一线程掌握的窗口时,系统必须做一次context switch,切换到另一线程去,调用该窗口函数,然后再做一次context switch切换回来,相对一般的函数调用而言,期间的额外负担较大。如果在MDI中,为每个子窗口分配一个线程,那么该子窗口的所有资源——包括画刷,DC,调色板等等都属于线程的资源。此时为线程做context switch时会代价很大