1. 程式人生 > >C++進階—>CreateEvent控制執行緒

C++進階—>CreateEvent控制執行緒

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;

}