第14章MFC 多線程程序設計
1. 三個觀念:模塊、進程、執行線程
一段可執行的程序(包括EXE 和DLL),其程序代碼、資料、資源被加載到內存中,由系統建置一個數據結構來管理它,就是一個模塊。
進程主要表達「擁有權」的觀念,執行線程則主要表達模塊中的程序代碼的「執行事實」。系統也是以一個特定的數據結構(Thread Database,TDB)記錄執行線程的所有相關資料,包括執行線程區域儲存空間(Thread Local Storage,TLS)、訊息隊列、handle 表格、地址空間(MemoryContext)等。
2. 執行線程排程(Scheduling)
排程器挑選「下一個獲得CPU 時間的執行線程」的唯一依據就是:執行線程優先權。
對於那些優先權本來就高的執行線程,也並不是有永久的保障權利。別忘了Windows 畢竟是個消息驅動系統,如果某個執行線程調用::GetMessage 而其消息隊列卻是空的,這個執
行線程便被凍結,直到再有消息進來爲止。凍結的意思就是不管你的優先權有多高,暫時
退出排班行列。執行線程也可能被以::SuspendThread強制凍結住(::ResumeThread 可以解
除凍結)。
會被凍結,表示這個執行線程「要去抓取消息,而執行線程所附帶的消息隊列中卻沒有消息」。
如果一個執行線程完全和UI 無關呢?是否它就沒有消息隊列?倒不是,但它的程序代碼中
沒有消息循環倒是事實。是的,這種執行線程稱爲workerthread。正因它不可能會被凍結,
所以它絕對不受Win16Mutex 或其它因素而影響其強制性多任務性質,及其優先權。
3. Worker Threads 和 UI Threads
從Windows 操作系統的角度來看,執行線程就是執行線程,並未再有什麼分類。但從MFC
的角度看,則把執行線程劃分爲和使用者接口無關的workerthreads,以及和使用者接口
(UI)有關的UI threads。
基本上,當我們以::CreateThread 產生一個執行線程,並指定一個執行線程函數,它就是一
個worker thread,除非在它的生命中接觸到了輸入消息-- 這時候它應該有一個消息回
路,以抓取消息,於是該執行線程搖身一變而爲UIthread。
4. MFC 多線程程序設計
A. 就像CWinApp 對象代表一個程序本身一樣,CWinThread 對象代表一個執行線程本身。
B. 雖然MFC 程序只會有一個CWinApp 對象,而CWinApp 衍生自CWinThread,但並不
是說一個MFC 程序只能有一個CWinThread 對象。每當你需要一個額外的執行線程,不
應該在MFC 程序中直接調用::CreateThread 或_beginthreadex,應該先產生一個
CWinThread 對象,再調用其成員函數CreateThread 或全域函數AfxBeginThread將執行
線程產生出來
C. 產生一個Worker Thread
CWinThread*pThread = AfxBeginThread(ThreadFunc, &Param);
...
UINTThreadFunc(LPVOID pParam)
{
...
}
執行線程函數是由系統調用的,也就是個callback函數,不容許有this 指針參數。所以任何一般的C++ 類別成員函數都不能夠拿來當做執行線程函式。它必須是個全域函數,或是個C++ 類別的static 成員函數。
D. 產生一個UI Thread
UI thread 可不能夠光由一個執行線程函數來代表,因爲它要處理消息,它需要一個消息回
路。好得很,CWinThread::Run 裏頭就有一個消息循環。所以,我們應該先從CWinThread
衍生一個自己的類別,再調用AfxBeginThread產生一個CWinThread 對象:
E. 執行線程的結束
既然worker thread 的生命就是執行線程函數本身,函數一旦return,執行線程也就結束了,
自然得很。或者執行線程函數也可以調用AfxEndThread,結束一個執行線程。
不論worker thread 或UI thread,都需要一個CWinThread 對象,當執行線程結
束,記得把該對象釋放掉(利用delete)。
F. 執行線程與同步控制
Windows 系統提供四種同步化機制,幫助程序進行這種工作:
1. Critical Section(關鍵區域)
2. Semaphore(號誌)
3. Event(事件)
4. Mutex(Mutual Exclusive,互斥器)