VC線程間通訊

1.使用全局變量
     實現線程間通信的方法有很多,常用的主要是通過全局變量、自定義消息和事件對象等來實現的。其中又以對全局變量的使用最爲簡潔。該方法將全局變量作爲線程監視的對象,並通過在主線程對此變量值的改變而實現對子線程的控制。
     由於這裏的全局變量需要在使用它的線程之外對其值進行改變,這就需要通過volatile關鍵字對此變量進行說明。使用全局變量進行線程通信的方法非常簡單,通過下面給出的示例代碼能夠對其有一個基本的認識。

                 // 線程通信用全局變量
                 volatile bool g_bDo = false;
                 ……
                 //線程處理函數
                 UINT ThreadProc5(LPVOID pParam)
                 {
                               //根據全局變量g_bDo的取值來決定線程的運行
                               while (g_bDo)
                               {            
                                            Sleep(2000);
                                            AfxMessageBox("線程正在運行!");
                               }

                               AfxMessageBox("線程終止");
                               return 0;
                 }
                 ……
                 void CSample06View::OnGlobalStart()
                 {
                               // 通過全局變量通知線程執行
                               g_bDo = true;

                               // 啓動線程   
                               AfxBeginThread(ThreadProc5, NULL);
                 }
                 void CSample06View::OnGlobalEnd()
                 {
                               // 通過全局變量通知線程結束
                               g_bDo = false;
                 }

2.利用自定義消息
     全局變量在線程通信中的應用多用在主線程對子線程的控制上,而從子線程向主線程的信息反饋則多采用自定義消息的方式來進行。這裏對自定義消息的使用同使用普通自定義消息非常相似,只不過消息的發送是在子線程函數中進行的。該方法的主體是自定義消息,應首先定義自定義消息並添加對消息的響應代碼。

                 // 自定義消息
                 #define WM_USER_MSG WM_USER + 101
                 ……
                 //消息響應函數在頭文件中的定義:
                 //{{AFX_MSG(CSample06View)
                 //}}AFX_MSG
                 afx_msg void OnUserMsg(WPARAM wParam, LPARAM lParam);
                 DECLARE_MESSAGE_MAP()
                 ……
                 //消息映射
                 BEGIN_MESSAGE_MAP(CSample06View, CView)
                 //{{AFX_MSG_MAP(CSample06View)
                 //}}AFX_MSG_MAP
                 ON_MESSAGE(WM_USER_MSG, OnUserMsg)
                 END_MESSAGE_MAP()
                 ……
                 //消息響應函數
                 void CSample06View::OnUserMsg(WPARAM wParam, LPARAM lParam)
                 {
                               // 報告消息
                               AfxMessageBox("線程已退出!");
                 }

     此後,在子線程函數需要向主線程發送消息的地方調用PostMessage()或SendMessage()消息傳遞函數將消息發送給主線程即可。由於消息發送函數是在線程中被調用,因此需要指出接受窗口句柄,可通過線程參數將其傳遞進線程函數。

                 UINT ThreadProc6(LPVOID pParam)
                 {
                               // 延遲一秒
                               Sleep(1000);

                               // 向主線程發送自定義消息
                               ::PostMessage((HWND)pParam, WM_USER_MSG, 0, 0);
                               return 0;
                 }
                 ……
                 void CSample06View::OnUseMessage()
                 {
                               // 獲取窗口句柄
                               HWND hWnd = GetSafeHwnd();

                               // 啓動線程
                               AfxBeginThread(ThreadProc6, hWnd);
                 }

3.使用事件內核對象
     利用事件(Event)內核對象對線程的通信要複雜些,主要通過對事件對象的監視來實現線程間的通信。事件對象由CreateEvent()函數來創建,具有兩種存在狀態:置位與復位,分別由SetEvent()和ResetEvent()來產生。事件的置位將通過WaitForSingleObject()或WaitForMultipleObjects()之類的通知等待函數繼續執行。

// 事件句柄
HANDLE hEvent = NULL;

UINT ThreadProc7(LPVOID pParam)
{
                 while(true)
                 {
                               // 等待事件發生
                               DWORD dwRet = WaitForSingleObject(hEvent, 0);
                               // 如果事件置位則退出線程,否則將繼續執行
                               if (dwRet == WAIT_OBJECT_0)
                                            break;
                               else
                               {
                                            Sleep(2000);
                                            AfxMessageBox("線程正在運行!");
                               }
                 }
               
                 AfxMessageBox("線程終止運行!");
                 return 0;
}
……
void CSample06View::OnEventStart()
{
                 // 創建事件   
                 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
               
                 // 啓動線程
                 AfxBeginThread(ThreadProc7, NULL);
}

void CSample06View::OnEventEnd()
{
                 // 事件置位
                 SetEvent(hEvent);             
}

     上面這段代碼展示了事件對象在線程通信中的作用。在創建線程前首先創建一個事件對象hEvent,這裏CreateEvent()函數所採用的四個參數分別表示句柄不能被繼承、事件在置位後將由系統自動進行復位、事件對象初始狀態爲復位狀態和不指定事件名。在創建的子線程中使用WaitForSingleObject()對hEvent進行監視。WaitForSingleObject()的函數原型爲:

                 DWORD WaitForSingleObject(
                               HANDLE hHandle,                             //等待對象的句柄
                               DWORD dwMilliseconds           //超過時間間隔
                 );

     函數將在hHandle對象有信號時或是在等待時間超出由dwMilliseconds設定的超時時間間隔返回。其返回值可以爲WAIT_ABANDONED、WAIT_OBJECT_0和WAIT_TIMEOUT,分別表示被等待的互斥量(Mutex)對象沒有被釋放、等待的對象信號置位和超時。通過對返回值的判斷可以區分出引起WaitForSingleObject()函數返回的原因。在本例中只關心WAIT_OBJECT_0的返回值,當通過SetEvent()將hEvent置位後即可使WaitForSingleObject()立即返回並通過跳出循環而結束線程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章