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()立即返回並通過跳出循環而結束線程。