Win32中,每个线程都有自己专属的消息队列,并不是每个窗口都有自己的消息队列。因为每个线程能够产生很多窗口,如果一个线程停止响应,或者它忙于一段耗时的计算工作,那么由它差生的窗口统统都会停止响应,但系统中的其他窗口还是继续正常的运作。
所有传递给某一窗口的消息,将由产生该窗口之线程负责处理。
对窗口所做的任何一个事情基本上都由该窗口的窗口函数处理,并因此被产生该窗口的线程处理。
SendMessage()
同一线程:直接调用窗口函数
不同线程:切换到新线程并调用窗口函数 ,在该函数返回之前,SendMessage不会返回(所以会发生意Context Switch,比一般函数调用费时间)
PostMessage()
同一线程:把消息放入消息队列立即返回
不同线程:立即返回,消息放入另一线程的消息队列中
----------------------------------------------------
所以如果各个窗口都有自己的线程,那么在处理各个窗口的响应时可能要发生很多次的context switch ,效率低下,所以不能有自己的线程。
如果一个线程调用了SendMessage后,当前线程就会等待其返回,类似于sleep状态,但是还是可以处理 外界对其窗口的sendMessage操作。
如果某个线程正在 处理由其他线程Send过来的消息,可以在该线程中调用IsSendMessage,会获得TRUE。为了让调用端线程能够继续工作,我们可以调用ReplyMessage。
为了防止线程因为SendMessage导致的死锁(永远醒不过来) ,Win32提供2个函数。第一个是SendMessageTimeout,允许你指定一个时间,时间终了了一定会返回。第二个是SendMessageCallback。这个函数会立刻返回。
------------------------------------------------
欲在一个MDI程序中有效率的使用多个线程,建议是以一个线程处理所有的用户输入以及用户界面的管理。用其他线程做一些后台的耗时操作。不管怎么做,主线程应该始终有回应。
线程之间的通讯
线程通常需要传递数据给另外一个线程,worker线程告诉别人他的工作做完了,GUI线程可以需要交给worker线程一个新工作。PostThreadMessage 类似于PostMessage,不过他的参数之一不是窗口的handle,而是线程ID,当然,接收端必须有消息队列。
DWORD idThread, //线程ID,可以有GetCurrentThreadID或CreateThread获得
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
返回值:如果成功的Post出去,返回TRUE。
以消息作为通讯方式,比起标准技术如全局变量等,有很大好处,比如目标线程很忙碌,则负责post的那个线程不会停滞下来。如果对象是同一进程的线程,可以自定义消息,如WM_APP+0X100等,并配置一块结构,防止你想要传递的数据。然后把结构指针当做lParam,接收端在处理完消息后负责释放内存。
锁住GDI对象
GDI对象被每个进程拥有,而被每个线程锁定。如果一个线程正在使用一个GDI对象,该对象将被锁定,其他线程没有办法在使用它。