等待執行緒WaitForSingleObject、WaitForMultipleObjects
等待執行緒WaitForSingleObject()、WaitForMultipleObjects()
在多執行緒下面,有時候我們會希望等待某一執行緒完成了再繼續做其他事情,要實現這個目的,可以使用Windows API函式WaitForSingleObject,或者WaitForMultipleObjects。這兩個函式都會等待Object被標為有訊號(signaled)時才返回的。 那麼,什麼是訊號呢? 簡單來說,Windows下建立的Object都會被賦予一個狀態量。如果Object被激活了,或者正在使用,那麼該Object就是無訊號,也就是不可用;另一方面,如果Object可用了,那麼它就恢復有訊號了。
這兩個函式的優點是它們在等待的過程中會進入一個非常高效沉睡狀態,只佔用極少的CPU時間片。(這兩個函式都是在核心狀態下等待核心物件,不切換到使用者模式下,因而效率很高)
WaitForSingleObject
1. 格式
DWORD WaitForSingleObject( HANDLEhHandle, DWORD dwMilliseconds);
有兩個引數,分別是THandle和Timeout(毫秒單位)。當等待仍在掛起狀態時,控制代碼被關閉,那麼函式行為是未定義的。該控制代碼必須具有 SYNCHRONIZE 訪問許可權,如果想要等待一條執行緒,那麼你需要指定執行緒的Handle,以及相應的Timeout時間。當然,如果你想無限等待下去,Timeout引數可以指定系統常量INFINITE
2. 引數
引數1:等待的物件:Event(事件),Mutex(互斥量),Semaphore(訊號量),Process(程序),Thread(執行緒) 等
引數2:dwMilliseconds為等待的時間,它有兩個具有特殊意義的值:0和INFINITE
3. 返回型別
有三種返回型別:
WAIT_OBJECT_0 0x00000000, 表示等待的物件有訊號(對執行緒來說,表示執行結束);
WAIT_TIMEOUT 0x00000102 等待超時,表示等待指定時間內,物件一直沒有訊號(執行緒沒執行完);
WAIT_ABANDONED 0x00000080當hHandle為mutex時,如果擁有mutex的執行緒在結束時沒有釋放核心物件會引發此返回值
示例:
#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <process.h>
using namespace std;
DWORD WINAPI FunProc(LPVOID lpParameter)
{
int i = 1;
for (; i < 1000; i++)
{
printf(" %d", i);
if (!(i % 10))
printf("\n");
}
return 0;
}
int main()
{
HANDLE hThread;
hThread = (HANDLE)CreateThread(NULL, 0, FunProc, NULL, 0, NULL);
DWORD dwRet = WaitForSingleObject(hThread, 1);
if (dwRet == WAIT_OBJECT_0)
{
printf("建立執行緒執行結束\n");
}
if (dwRet == WAIT_TIMEOUT)
{
printf("等待超時\n");
}
if (dwRet == WAIT_ABANDONED)
{
printf("Abandoned\n");
}
CloseHandle(hThread);
return 0;
}
WaitForMultipleObjeccts
相對來說,WaitForMultipleObjects要複雜點點
1. 格式:
DWORD WaitForMultipleObjects(
DWORD nCount,
CONST HANDLE * lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds);
WaitForMultipleObjects是Windows中的一個功能非常強大的函式,幾乎可以等待Windows中的所有的核心物件
2. 引數:
引數1:用於指定控制代碼陣列的數量,物件控制代碼的最大數量為MAXIMUM_WAIT_OBJECTS。此引數不能為零
引數2:一組物件控制代碼中用於指定控制代碼陣列的記憶體地址,該陣列可以包含不同型別物件的控制代碼。它可能不包含同一控制代碼的多個副本。如果其中一個控制代碼在等待仍處於暫掛狀態時關閉,則該函式的行為未定義。控制代碼必須具有SYNCHRONIZE訪問許可權。
引數3:如果此引數為TRUE則在lpHandles陣列中的所有物件的狀態發出訊號時,該函式返回。如果為FALSE,則當任何一個物件的狀態設定為訊號時,該函式返回。在後一種情況下,返回值表示其狀態導致函式返回的物件。
引數4:用於指定等待的Timeout時間,單位毫秒,設定為0則它立即返回,是INFINITE則僅在發出指定物件訊號時才返回該函式。
3. 返回值:
WAIT_OBJECT_0到(WAIT_OBJECT_0 + nCount - 1如果bWaitAll為TRUE),則返回值表明所有指定物件的狀態訊號。如果bWaitAll為FALSE,則返回值減去WAIT_OBJECT_0表示lpHandles陣列的序號。如果同時有多個核心物件被觸發,這個函式返回的只是其中序號最小的那個。
WAIT_ABANDONED_0至(WAIT_ABANDONED_0 + nCount - 1)如果bWaitAll為TRUE,則返回值表明所有指定物件的狀態是觸發的,並且至少物件之一,是一個廢棄的互斥物件。如果bWaitAll為FALSE,則返回值減去WAIT_ABANDONED_0 表示一個廢棄的互斥物件在lpHandles陣列中的下標
WAIT_TIMEOUT表示時間隔已過,由bWaitAll引數指定的條件得不到滿足
WAIT_FAILED則表示函式執行失敗,會設定GetLastError
WAIT_IO_COMPLETION:(僅適用於WaitForMultipleObjectsEx)由於一個I/O完成操作已作好準備執行,所以造成了函式的返回
#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <process.h>
using namespace std;
HANDLE m_hEvent[2];
DWORD WINAPI Fun(LPVOID pM)
{
while (1)
{
int i;
}
return 0;
}
DWORD WINAPI Fun1(LPVOID pM)
{
//while (1)
//{
int i;
//}
return 0;
}
int main()
{
m_hEvent[0] = (HANDLE)CreateThread(NULL, 0, Fun, NULL, 0, NULL);
m_hEvent[1] = (HANDLE)CreateThread(NULL, 0, Fun1, NULL, 0, NULL);
int index = WaitForMultipleObjects(2, m_hEvent, FALSE, 500);
if (index == WAIT_OBJECT_0)
{
cout << "Fun返回" << endl;
}
else if (index == WAIT_OBJECT_0 + 1)
{
cout << "Fun1返回" << endl;
}
else if (index == WAIT_TIMEOUT)
{
cout << "超時" << endl;
}
return 0;
}
注意:
舉個例子:我們需要在一個執行緒中處理從完成埠、資料庫、和可等待定時器來的資料。一個典型的實現方法就是:用WaitForMultipleObjects等待所有的這些事件。如果完成埠,資料庫發過來的資料量非常大,可等待定時器時間也只有幾十毫秒。那麼這些事件同時觸發的機率可以說非常大,我們不希望丟棄任何一個被觸發的事件。那麼如何能高效地實現這一處理呢?多個核心物件被觸發時,WaitForMultipleObjects選擇其中序號最小的返回。而WaitForMultipleObjects它只會改變使它返回的那個核心物件的狀態。這兒又會產生一個問題,如果序號最小的那個物件頻繁被觸發,那麼序號比它大的核心物件將得不到被處理的機會。為了解決這一問題,可以採用雙WaitForMultipleObjects檢測機制來實現。見下面的例子:
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
DWORD dwRet = 0;
int nIndex = 0; //nIndex相當於陣列pHandles的下標
while (1)
{
//引數3設定為FALSE則有滿足的物件就返回
dwRet = WaitForMultipleObjects(nCount, pHandles, FALSE, INFINITE);
switch (dwRet)
{
case WAIT_TIMEOUT:
break;
case WAIT_FAILED:
return 1;
default:
{
nIndex = dwRet - WAIT_OBJECT_0;
ProcessHanlde(nIndex++); //做某個執行緒結束後的相關操作處理的函式,然後將下標值nIndex進行++
//繼續按照陣列的方向向下檢測
while (nIndex < nCount) //nCount事件物件總數
{
//繼續檢測陣列中除了第一個已經返回的執行緒,剩下執行緒的返回情況,引數1:nCount-nIndex即為剩下的數量
//引數2:為剩下執行緒中的首地址
dwRet = WaitForMultipleObjects(nCount - nIndex, &pHandles[nIndex], FALSE, 0);
switch (dwRet)
{
case WAIT_TIMEOUT:
nIndex = nCount; //退出檢測,因為沒有被觸發的物件了,相當於已經從頭到尾進行過一遍檢測以及處理
break;
case WAIT_FAILED:
return 1;
default:
{
//因為nIndex是相對於整個陣列的下標,所以要進行這樣的賦值,然後繼續沿陣列方向進行檢測
nIndex = nIndex + dwRet - WAIT_OBJECT_0;
ProcessHanlde(nIndex++);
}
break;
}
}
}
break;
}
}
return 0;
}