1.UI線程
2.工作線程
把Delphi里TThread的WaitFor函數轉化成C++代碼,就會是下面這個樣子。
- BOOL TThread::WaitFor(HANDLE hThread)
- {
- MSG msg;
- HANDLE handle[1];
- handle[0] = hThread;
- DWORD dwWaitResult = 0;
- do
- {
- // This prevents a potential deadlock if the background thread
- // does a SendMessage to the foreground thread
- if (dwWaitResult == WAIT_OBJECT_0 + 1)
- PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE);
- dwWaitResult = MsgWaitForMultipleObjects(1, handle, false, 1000, QS_SENDMESSAGE);
- if (dwWaitResult == WAIT_FAILED)
- return FALSE;
- if (dwWaitResult == WAIT_TIMEOUT)
- {
- TerminateThread(hThread, 0);
- return FALSE;
- }
- }
- while (dwWaitResult != WAIT_OBJECT_0);
- return TRUE;
- }
使我疑惑的是這兩行代碼
- if (dwWaitResult == WAIT_OBJECT_0 + 1)
- PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE);
如果注釋所講是真的,那究竟在哪里處理了其它線程用SendMessage發送過來的消息呢?於是我翻MSDN,查PeekMessage函數,它有這樣的解釋:
“The PeekMessage function dispatches incoming sent messages, checks the thread message queue for a posted message, and retrieves the message (if any exist).”
我用的是MSND2005,好像再早些的版本里並無“dispatches incoming sent messages”這句。光看這樣的注釋,實在是太晦澀了些。想起我原來好像發過個討論這問題的帖子,翻了下發帖記錄,還真有,見http://topic.csdn.net/u/20070824/14/d0c96e50-6348-49d3-98e4-9fcc9ead5fdd.html。我當時找到的答案是這樣的:
“找到答案了。答案在<<Programming Applications for Microsoft Windows>>第26章的Waking a Thread一節.
The Algorithm for Extracting Messages from a Thread 's Queue
...
1. If the QS_SENDMESSAGE flag is turned on, the system sends the message to the proper window procedure. Both the GetMessage and PeekMessage functions handle this processing internally and do not return to the thread after the window procedure has processed the message; instead, these functions sit and wait for another message to process.
我試着翻譯一下:如果QS_SENDMESSAGE標志位有效,系統發送消息(即其它線程通過SendMessage發送過來的消息)到對應的窗口過程。GetMessage和PeekMessage函數在內部進行這樣的處理,一直等到窗口過程處理完之后才會返回。也就是說,這些函數(GetMessage和PeekMessage)坐下來等到其它的消息處理完。”
書本里的解釋已經很清楚詳細了,我當時的譯文有錯誤,原文是說GetMessage和PeekMessage調用窗口過程處理完SendMessage發送過來的消息后,還會繼續坐下來等自己消息隊列里的消息。
理論有了,結合着看看上面WaitFor的代碼。MsgWaitForMultipleObjects的最后一個參數QS_SENDMESSAGE指明了如果其它有其它線程用SendMessage向本線程的窗口發送消息,MsgWaitForMultipleObjects就會立即返回,返回值為WAIT_OBJECT_0 + nCount(本例中nCount值為1),接下來就輪到PeekMessage登場了。
PeekMessage其實做了兩件事,一件是把收到的消息標示為舊消息。見MSND里對MsgWaitForMultipleObjects返回值的解釋:
“Functions such as PeekMessage, GetMessage, and WaitMessage mark messages in the queue as old messages.”
另一件就是把SendMessage發送過來的消息dispatch到窗口過程。下一次調用MsgWaitForMultipleObjects時隊列里便沒有新消息了。
那變舊的消息還在隊列里嗎?答案是不在。因為用SendMessage發送過來的消息根本就不會進應用程序的消息隊列。在PeekMessage調用窗口過程處理完它時,它便消失了。而我們也很輕松的就知道,SendMessage函數把QS_SENDMESSAGE這個標志turn on了,PeekMessage和GetMessage還有WaitMessage把這個標志turn off了,把QS_SENDMESSAGE標志turn off的過程就是把消息變舊的過程。