與工作者線程不同的是,使用介面執行緒通常用於處理使用者的輸入,響應使用者產生的訊息。為了建立一個使用介面執行緒,需要從CWinThread引出自訂的類,並將該類的運行時資訊傳給使用者介面形式的AfxBeginThread函數,以便建立使用介面執行緒。 一般來說,使用介面執行緒的建立過程需要先從基類CWinThread中派生出使用者自訂的新類,再使用AfxBeginThread函數建立線程。 【範例17-1】下面將通過一個執行個體來講述如何建立一個使用介面執行緒。該執行個體實現建立一個獨立於應用程式的視窗,其具體操作步驟如下所述。 建立一個MFC單文檔應用程式。單擊【File】|【New】功能表項目,開啟建立對話方塊,選擇建立【MFC AppWizard[exe]】工程,為工程命名為“建立線程”。在AppWizard的第一步中選中【Single document】,其他步驟按照預設設定,單擊【Finish】按鈕完成建立。 添加功能表項目。為了示範出該執行個體建立獨立於應用程式的使用介面執行緒,在該工程中添加新的功能表項目“使用介面執行緒”用於建立和啟動線程。具體操作為: 開啟工作區的【ResourceView】選項卡,修改MFC應用程式嚮導自動產生的菜單資源。在原菜單基礎上再添加一個彈出式菜單“線程”。在其下面添加功能表項目“使用介面執行緒”,設定其ID為ID_THREAD_GUI,17-3所示。 從CWinThread中派生新類。派生新類的方法很簡單,在以前章節中也介紹過。可以使用ClassWizard工具單擊【Add Class】|【New】命令,也可以通過開啟工作區【ClassView】選項卡。按右鍵最頂層類集,在彈出的捷徑菜單中選擇【New Class】功能表項目來啟動【New Class】對話方塊。在基類列表框中選擇CWindThread類,然後在【Name】文字框中輸入衍生類別名。該執行個體定義其衍生類別為MyThread。 單擊【OK】按鈕後即建立了CWinThread類的衍生類別MyThread。此時讀者在工作台的【FileView】標籤中找到MyThread.h檔案,開啟後可看到如下的類定義。 // MyThread thread class MyThread : public CWinThread { DECLARE_DYNCREATE(MyThread) protected: MyThread(); …… DECLARE_MESSAGE_MAP() }; 說明:代碼中使用了DECLARE_DYNCREATE宏,使用該宏表明MyThread類具有動態建立的能力。使用DECLARE_MESSAGE_MAP宏表明具有訊息映射,可以處理命令訊息。 再開啟MyThread類的實現檔案(MyThread.cpp)。下面列出其中一部分代碼。 // MyThread IMPLEMENT_DYNCREATE(MyThread, CWinThread) MyThread::MyThread() { } …… BEGIN_MESSAGE_MAP(MyThread, CWinThread) END_MESSAGE_MAP() 提示:代碼中使用了IMPLEMENT_DYNCREATE宏是和標頭檔中DECLARE_ DYNCREATE宏相對應的,其保證建立的類具有動態建立的能力。而BEGIN_MESSA GE_MAP和END_MESSAGE_MAP宏是和標頭檔中DECLARE_MESSAGE_MAP宏相對應的,使類具有訊息映射機制。 重載CWinThread的虛函數。基類CWinThread有一些可以重載的虛函數。其中InitInstance函數必須要重載,該函數用於初始化執行個體。ExitInstance函數一般情況下也應該重載,該函數用於清除執行個體。而Run函數,如果沒有特殊需要,一般不去重載。該函數負責將訊息分發出去。重載InitInstance函數如範例程式碼17-1所示。 範例程式碼17-1 01 BOOL MyThread::InitInstance() 02 { 03 // TODO: perform and per-thread initialization here 04 CFrameWnd* pFrameWnd= new CFrameWnd(); //定義架構對象 05 pFrameWnd->CreateEx(0,AfxRegisterWndClass( CS_HREDRAW|CS_VREDRAW) , 06 "使用介面執行緒樣本", 07 WS_OVERLAPPEDWINDOW|WS_VISIBLE, 08 CRect(100,100,400,300), 09 NULL, 10 0); //建立視窗 11 m_pMainWnd=pFrameWnd; 12 pFrameWnd->ShowWindow(SW_SHOW); //顯示視窗 13 pFrameWnd->UpdateWindow(); 14 return TRUE; 15 } 使用AfxBeginThread函數建立並啟動線程對象。在CMyView類中,添加建立功能表項目的COMMAND命令訊息處理函數 OnThreadGui。添加方法為:按右鍵CMyView類,選擇【Add Windows Message Handle】功能表項目,進入添加訊息處理函數對話方塊。在【Class or Object IDS】下拉式清單方塊中選擇功能表項目ID_Thread_Gui,事件則選擇【COMMAND】並雙擊。 單擊【OK】按鈕添加完成。單擊【Edit Existing】按鈕進入代碼編輯器介面。在該訊息處理函數中通過調用AfxBeginThread函數來啟動使用介面執行緒,代碼如下: void CMyView::OnThreadGui() { // TODO: Add your command handler code here AfxBeginThread(RUNTIME_CLASS(MyThread)); //啟動使用介面執行緒 } 代碼中又用到了AfxBeginThread函數,但是建立工作者線程和建立使用介面執行緒的AfxBeginThread函數的形式是有一定區別的。在這段代碼裡,用的是建立使用介面執行緒的AfxBeginThread函數。 警告:因為在視圖類CMyView中用到了建立的線程類MyThread。因此,最後不要忘記在CMyView類中包含MyThread類的標頭檔。具體代碼如下: #include "MyThread.h" 【運行結果】在Visual C++ 6.0中編譯後無錯誤,使用快速鍵【Ctrl+F5】運行該工程。 【代碼解析】在上述代碼中,第4行首先構造了一個架構視窗對象。然後在第5~10行調用其成員函數CreateEx來產生視窗。最後將視窗顯示出來。如果使用介面執行緒正常執行時,就會產生一個標題為“使用介面執行緒樣本”的視窗。讀者可以發現,建立的使用介面執行緒視窗顯示在螢幕上。 說明:需要讀者瞭解的是,這個線程視窗不屬於建立線程程式的主架構視窗,而是和主架構視窗(也可以說是主線程視窗)並列的。兩者的父視窗都是系統的案頭。查看案頭底部的Windows系統工作列就會發現,除了建立線程應用程式視窗,也有單獨的使用介面執行緒視窗。 此時,這兩個視窗可以並行工作,互不影響。使用者可以單獨關閉子視窗使用介面執行緒視窗,就相當於正常退出了該子線程視窗,不會對主線程建立進程視窗造成影響。反過來,如果使用者關閉主線程視窗,則使用介面執行緒視窗也會被迫關閉。這種情況屬於使用介面執行緒非正常退出,會造成記憶體流失。 |