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;

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章