一、簡介
互鎖函數家族只能在單值上運行,根本無法使線程進入等待狀態;關鍵程式碼片段只適用於對單個進程中的線程實施同步;我們可以使用核心對象來實現線程同步。
二、等待函數
使用等待函數可以使線程進入等待狀態,直到一個特定的核心對象變為已通知狀。
(1)、等待單個事件
DWORD WaitForSingleObject(
HANDLE hObject, //該函數等待的物件控點
DWORD dwMilliseconds); //等待逾時時間
(2)、等待多個事件
DWORD WaitForMutipleObjects(
DWORD dwCount, //需要等待的核心數量
CONST HANDLE *phObjects, //核心物件控點數組的指標
BOOL fWaitAll, //是否等待所有對象
DWORD dwMilliseconds); //等待逾時時間
註:
當fWaitAll為TRUE時:線程進入等待狀態,直到所有指定的核心對象都變為已通知狀態。
當fWaitAll為FALSE時:線程進入等待狀態,直到指定核心對象中的任何一個變為已通知狀態。
(3)、等待函數傳回值
WAIT_OBJECT_0: 所等待的對象變為已通知狀態
WAIT_TIMEOUT: 等待逾時
WAIT_FILED: 出現錯誤
三、事件核心對象
1、基本概念
事件核心對象是一基本核心對象,他能夠通知一個操作已經完成,與其他核心對象一樣,包含一個使用計數。該計數為BOOL型變數,用於指明該事件是否處於已通知狀態還是未通知狀態。另一計數用於指明該事件是自動重設事件還是人工重設事件。
2、事件的類型
(1)、自動重設事件
當自動重設事件得到通知時,等待該事件的線程只有一個變為可調度線程。
(2)、人工重設事件
當人工重設事情得到通知時,等待該事件的所有線程均變為可調度線程。
註:
當使用自動重設事件時,系統只允許一個輔助線程變成可調度狀態,我們無法控制系統使哪個線程可調度,哪個線程等待。
當人工重設時由程式員來決定何時設定事件為已通知狀態或未通知狀態,當自動重設時,當等待的事件發生後,系統自動將事件置為相反的狀態。無需程式員調用ResetEvent函數。
3、建立事件核心對象
HANDLE CreateEvent(
PSECURITY_ATTRIBUTES psa, //安全屬性
BOOL fManualReset, //是否人工重設
BOOL fInitialState, //初始化未已通知還是未通知
PCTSTR pszName); //命名核心對象的名字
4、開啟事件核心對象
HANDLE OpenEvent(
DWORD fdwAccess,
BOOL fInherit,
PCTSTR pszName);
5、設定事件對象未已通知狀態
BOOL SetEvent(
HANDLE hEvent);
6、設定事件為未通知狀態
BOOL ResetEvent(
HANDLE hEvent);
四、等待定時器核心對象
1、基本用途
等待定時器是再某個時間或按規定的間隔時間發出自己的訊號通知的核心對象,通常用來再某個時間執行某個操作。
等待定時器對象總是再未通知狀態建立,必須調用SetWaitableTimer函數來告訴定時器何時成為已通知狀態。
2、建立等待定時器核心對象
HANDLE CreateWaitableTimer(
PSECURITY_ATTRIBUTES psa, //安全屬性
BOOL fManualReset, //是人工重設還是自動重設
PCTSTR pszName); //命名核心對象的名稱
3、開啟等待定時器核心對象
HANDLE OpenWaitableTimer(
DWORD dwDesiredAccess, //訪問屬性
BOOL bInheritHandle, //可繼承性
PCTSTR pszName); //命名核心對象的名稱
4、設定等待定時器為已通知狀態
BOOL SetWaitableTimer(
HANDLE hTimer, //要設定的定時器控制代碼
Const LARGE_INTEGER *pDueTime, //第一次報時的時刻
LONG lPeriod, //報時間隔
PTIMERAPCROUTINE pfnCompletionRoutine, //通常設為NULL
PVOID pvArgToCompletionRoutine, //通常設為NULL
BOOL fResume); //通常設為FALSE
5、撤銷定時器
BOOL CancelWaitableTimer(
HANDLE hTimer); //需撤銷的定時器的控制代碼
簡單例子:
#include <windows.h>
#include <process.h>
#include <iostream>
using namespace std;
HANDLE g_putApple;
HANDLE g_putBanana;
HANDLE g_eatApple;
HANDLE g_eatBanana;
HANDLE g_dish;
HANDLE m_hmtx;
/******************************************************************
*父親放蘋果,兒子吃蘋果,母親放香蕉,女兒吃香蕉
*在一個時間盤子之中只能放一樣水果,蘋果或香蕉
*****************************************************************/
//放蘋果
UINT WINAPI PutAppleThread(PVOID pvParam)
{
for(int i = 0; i < 10; i++)
{
WaitForSingleObject(g_dish, INFINITE);
WaitForSingleObject(g_putApple, INFINITE);
cout << "put apple!/n";
SetEvent(g_eatApple);
Sleep(5);
}
return 0;
}
//放香蕉
UINT WINAPI PutBananaThread(PVOID pvParam)
{
for(int i = 0; i < 10; i++)
{
WaitForSingleObject(g_dish,INFINITE);
WaitForSingleObject(g_putBanana, INFINITE);
cout << "put banana!/n";
SetEvent(g_eatBanana);
}
return 0;
}
//吃蘋果
UINT WINAPI EatAppleThread(PVOID pvParam)
{
for(int i = 0; i < 10; i++)
{
WaitForSingleObject(g_eatApple, INFINITE);
cout << "eat apple!/n";
SetEvent(g_putApple);
SetEvent(g_dish);;
}
return 0;
}
//吃香蕉
UINT WINAPI EatBananaThread(PVOID pvParam)
{
for(int i = 0; i < 10; i++)
{
WaitForSingleObject(g_eatBanana, INFINITE);
cout << "eat banana!/n";
SetEvent(g_putBanana);
SetEvent(g_dish);
Sleep(6);
}
return 0;
}
int main()
{
g_putApple = CreateEvent(NULL, FALSE, TRUE, NULL);
g_putBanana = CreateEvent(NULL, FALSE, TRUE, NULL);
g_eatApple = CreateEvent(NULL, FALSE, FALSE, NULL);
g_eatBanana = CreateEvent(NULL, FALSE, FALSE, NULL);
g_dish = CreateEvent(NULL, FALSE, TRUE, NULL);
m_hmtx = CreateMutex(NULL, FALSE, NULL);
HANDLE hThread[4];
int x;
hThread[0] = (HANDLE)_beginthreadex(NULL, 0, PutAppleThread, (void *)&x, 0, NULL);
hThread[1] = (HANDLE)_beginthreadex(NULL, 0, PutBananaThread, (void *)&x, 0, NULL);
hThread[2] = (HANDLE)_beginthreadex(NULL, 0, EatAppleThread, (void *)&x, 0, NULL);
hThread[3] = (HANDLE)_beginthreadex(NULL, 0, EatBananaThread, (void *)&x, 0, NULL);
char ch;
cin >> ch;
return 0;
}