C++進階—>CreateEvent控制執行緒
阿新 • • 發佈:2019-02-16
1.概述
事件物件就像一個開關:它只有兩種狀態---開和關。當一個事件處於”開”狀態,我們稱其為”有訊號”否則稱為”無訊號”。可以在一個執行緒的執行函式中建立一個事件物件,然後觀察它的狀態,如果是”無訊號”就讓該執行緒睡眠,這樣該執行緒佔用的CPU時間就比較少。
2.CreateEvent函式
產生事件物件的函式如下: HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, //一般為NULL,屬性
BOOL bManualReset, // 重置型別,手動重置還是 自動重置
BOOL bInitialState, // 初始狀態,TRUE為有訊號,FALSE為無訊號
LPCTSTR lpName // 事件物件名
);
該函式建立一個Event同步物件,如果CreateEvent呼叫成功的話,會返回新生成的物件的控制代碼,否則返回NULL。
引數說明:
lpEventAttributes 一般為NULL
bManualReset 建立的Event是自動復位還是人工復位.如果true,人工復位, 一旦該Event被設定為有訊號,則它一直會等到ResetEvent()被呼叫時才會恢復為無訊號; 如果為false,Event被設定為有訊號,則當有一個wait到它的Thread時, 該Event就會自動復位,變成無訊號. 如果想在每次呼叫WaitForSingleObject 後讓WINDOWS為您自動地把事件地狀態恢復為”無訊號”狀態,必須把該引數設為FALSE,否則,您必須每次呼叫ResetEvent函式來清除事件的訊號。
bInitialState 初始狀態,true,有訊號,false無訊號
lpName 事件物件的名稱。您在OpenEvent函式中可能使用。
3.Event相關
一個Event被建立以後,可以用OpenEvent()來獲得它的Handle,用CloseHandle()來關閉它(在主執行緒中使用CloseHandle()函式,很有可能導致子執行緒的Event物件訊號控制機制失效,但需要將Event關閉,否則容易造成控制代碼洩漏問題,故合理使用關閉控制代碼函式);用SetEvent()或PulseEvent()來設定它使其有訊號,用ResetEvent() 來使其無訊號,用WaitForSingleObject()或WaitForMultipleObjects()來等待其變為有訊號.SetEvent(HANDLE hEvent ); PulseEvent(HANDLE hEvent ); ResetEvent(HANDLE hEvent );
PulseEvent()是一個比較有意思的使用方法,正如這個API的名字,它使一個Event 物件的狀態發生一次脈衝變化,從無訊號變成有訊號再變成無訊號,而整個操作是原子的.對自動復位的Event物件,它僅釋放第一個等到該事件的thread(如果有),而對於人工復位的Event物件,它釋放所有等待的thread.
這裡有兩個API函式用來修改事件物件的訊號狀態:SetEvent和ResetEvent。前者把事件物件設為”有訊號”狀態,而後者正好相反。在事件物件生成後,必須呼叫WaitForSingleObject來讓執行緒進入等待狀態,該函式的語法如下:
WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);
hHandle-->指向同步物件的指標。事件物件其實是同步物件的一種。
dwMilliseconds--> 等待同步物件變成”有訊號”前等待的時間,以毫秒計。當等待的時間超過該值後無訊號同步物件仍處於”無訊號”狀態,執行緒不再等待, WaitForSingleObject函式會返回WAIT_TIMEOUT。如果想要執行緒一直等待,請把該引數設為INFINITE(該值等於0xffffffff)為一直等待。
4.示例
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <string.h>
#include <sstream>
using namespace std;
#define NAME_LINE 40
HANDLE g_hMutex;
HANDLE g_hEvent;
//定義執行緒函式傳入引數的結構體
typedef struct __TICKET
{
int nCount;
char strTicketName[NAME_LINE];
__TICKET() : nCount(0)
{
memset(strTicketName, 0, NAME_LINE * sizeof(char));
}
}TICKET;
typedef struct __THD_DATA
{
TICKET* pTicket;
char strThreadName[NAME_LINE];
__THD_DATA() : pTicket(NULL)
{
memset(strThreadName, 0, NAME_LINE * sizeof(char));
}
}THD_DATA;
//基本型別資料轉換成字串
template<class T>
string convertToString(const T val)
{
string s;
stringstream ss;
ss << val;
ss >> s;
return s;
}
//售票程式
DWORD WINAPI SaleTicket(LPVOID lpParameter)
{
THD_DATA* pThreadData = (THD_DATA*)lpParameter;
TICKET* pSaleData = pThreadData->pTicket;
while(pSaleData->nCount > 0)
{
//請求獲得一個互斥量鎖
WaitForSingleObject(g_hEvent,INFINITE);
WaitForSingleObject(g_hMutex, INFINITE);
if (pSaleData->nCount > 0)
{
cout << pThreadData->strThreadName << "出售第" << pSaleData->nCount -- << "的票,";
if (pSaleData->nCount >= 0) {
cout << "出票成功!剩餘" << pSaleData->nCount << "張票." << endl;
} else {
cout << "出票失敗!該票已售完。" << endl;
}
}
//Sleep(10);
//釋放互斥量鎖
ReleaseMutex(g_hMutex);
}
return 0L;
}
//售票系統
int _tmain(int argc, _TCHAR* argv[])
{
//建立一個互斥量
g_hMutex = CreateMutex(NULL, FALSE, NULL);
//建立一個控制量
g_hEvent = CreateEvent(NULL,TRUE,TRUE,NULL);
//初始化火車票
TICKET ticket;
ticket.nCount = 100;
strcpy(ticket.strTicketName, "北京-->贛州");
//定義售票口陣列 和 執行緒陣列
const int THREAD_NUMM = 8;
THD_DATA threadSale[THREAD_NUMM];
HANDLE hThread[THREAD_NUMM];
//迴圈售票
for(int i = 0; i < THREAD_NUMM; ++ i)
{
threadSale[i].pTicket = &ticket;
string strThreadName = convertToString(i);
strThreadName = "視窗" + strThreadName;
strcpy(threadSale[i].strThreadName, strThreadName.c_str());
//建立執行緒
hThread[i] = CreateThread(NULL, NULL, SaleTicket, &threadSale[i], 0, NULL);
//請求獲得一個互斥量鎖
if (i==0)
{
WaitForSingleObject(g_hMutex, 10);
cout << "開始出售 " << threadSale[i].pTicket->strTicketName << " 的票..." << endl;
//釋放互斥量鎖
ReleaseMutex(g_hMutex);
}
if (i==6)
{
SetEvent(g_hEvent);
//PulseEvent(g_hEvent);
//ResetEvent(g_hEvent);
}
//關閉執行緒
CloseHandle(hThread[i]);
}
//CloseHandle(g_hEvent);
system("pause");
return 0;
}