线程与消息队列
当一个线程第一次被创建时,系统假定线程不会用于任何与用户相关的任务。这样可以减少线程对系统资源的要求。但是,一旦该线程调用一个与图形用户界面有关的函数 ( 如检查它的消息队列或建立一个窗口 ),系统就会为该线程分配一些另外的资源,以便它能够执行与用户界面有关的任务。特别是,系统分配了一个THREADINFO结构,并将这个数据结构与线程联系起来。
THREADINFO结构体如下:
1.将消息发送到线程的消息队列
当线程有了与之联系的THREADINFO结构时,消息就有自己的消息队列集合。
通过调用函数 BOOL PostMesssage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
可以将消息放置在线程的登记消息队列中。
当一个线程调用这个函数时,系统要确定是哪个线程建立了用 hwnd 参数标识的窗口。然后系统分配一块内存,将这个消息参数存储在这块内存中,并将这块内存增加到相应线程的登记消息队列中。并且该函数还设置QS_POSTMESSAGE唤醒位。函数 PostMesssage 在登记了消息后立即返回,调用该函数的线程不知道登记的消息是否被指定窗口的窗口过程所处理。
还可通过调用函数 BOOL PostThreadMesssage(DWORD dwThreadId, UINT uMsg, WPARAM wParam, LPARAM lParam) 将消息放置在线程的登记消息队列中,同 PostMesssage 函数一样,该函数在向线程的队列登记消息后立即返回,调用该函数的线程不知道消息是否被处理。
向线程的队列发送消息的函数还有 VOID PostQuitMesssage(int nExitCode) ;
该函数可以终止线程消息的循环,调用该函数类似于调用:PostThreadMesssage(GetCurrenThreadId( ), WM_QUIT, nExitCode, 0); 但 PostQuitMesssage 并不实际登记一个消息到任何队列中。只是在内部,该函数设定 QS_QUIT 唤醒标志,并设置 THREADINFO 结构的 nExitCode 成员。
2.向窗口发送消息
将窗口消息直接发送给一个窗口过程可以使用函数 LRESULT SendMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 窗口过程将处理这个消息,只有当消息被处理后,该函数才能返回。即具有同步的特性。
该函数的工作机制:
2.1 如果调用该函数的线程向该线程所建立的窗口发送了一个消息,SendMessage 就很简单:它只是调用指定窗口的窗口过程,将其作为一个子例程。当窗口过程完成对消息的处理时,它向 SendMessage 返回一个值。SendMessage 再将这个值返回给调用线程。
2.2 当一个线程向其他线程所建立的窗口发送消息时,SendMessage 就复杂很多(即使两个线程在同一个进程中也是如此)。windows 要求建立窗口的线程处理窗口的消息。所以当一个线程调用 SendMessage 向一个由其他进程所建立的窗口发送一个消息,也就是向其他线程发送消息,发送线程不可能处理该窗口消息,因为发送线程不是运行在接收进程的地址空间中,因此不能访问相应窗口的过程的代码和数据。(对于这个,我有点疑问:同一个进程的不同线程是运行在相同进程的地址空间中,它也采用这种机制,又作何解释呢?)实际上,发送线程要挂起,而有另外的线程处理消息。所以为了向其他线程建立的窗口发送一个窗口消息,系统必须执行一些复杂的动作。
由于windows使用上述方法处理线程之间的发送消息,所以有可能造成线程挂起,严重的会出现死锁。
利用一下4个函数可以编写保护性代码防护出现这种情况。
1. LRESULT SendMessageTimeout( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT fuFlags, UINT uTimeout , PDWORD_PTR pdwResult);
2. BOOL SendMessageCallback( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, SENDSYNCPROC pfnResultCallback, ULONG_PTR dwData);
3. BOOL SendNotifyMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
4.BOOL ReplyMessage( LRESULT lResult);
另外可以使用函数 BOOL InSendMessage( ) 判断是在处理线程间的消息发送,还是在处理线程内的消息发送