目錄
一、前言
二、基礎用法
三、API詳解
1.創建事件對象
2控制事件狀態
3.等待事件對象:
四、實戰案例
1.案例描述?
2.代碼設計
?3.總設計代碼
4.運行結果
一、前言
????????事件對象(Event Object)是我們在大型項目中,進行多線程同步處理的時候經常用到的一種內核對象,下面我就根據它的基礎本身的特點和相關的API函數,與實戰案例相結合,講述它的基礎理論和用法。
二、基礎用法
????????在Windows編程中,事件對象(Event Objects)是一種內核對象,主要用于線程之間的同步。當多個進程需要訪問共享資源時,可以通過CreateEvent創建的事件對象來控制訪問順序,避免資源沖突和數據不一致的問題。
????????事件對象中經常結合進行使用的有以下四種api函數,我們掌握了這四種API函數的基本用法,可以說就掌握了事件對象(Event Object)。以下api函數分別為 CreateEvent ,?SetEvent,
ResetEvent 和?WaitForSingleObject。后面我會依次講解各個api函數的原型以及作用。
三、API詳解
1.創建事件對象
? ? ? ? ? ?我們可以使用 CreateEven 函數來創建一個事件對象,它是一個Windows API函數,這個函數允許你指定事件對象的安全屬性、是手動重置還是自動重置、以及它的初始狀態(信號態或非信號態)。下面是它的原型:
HANDLE WINAPI CreateEventW(_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, //安全屬性_In_ BOOL bManualReset, // 復位方式:true 必須用 resetevent手動復原 false 自動還原為無信號狀態_In_ BOOL bInitialState, //初始狀態 : true 初始狀態為有信號狀態: false 無信號狀態_In_opt_ LPCWSTR lpName //對象名稱: null 無名的事件對象);
? ? ? ? 可以根據原型解釋,它的返回值類型為 句柄(空指針) ,函數約束類型為 WINAPI (_stdcall)。
????????第一個參數為??lpEventAttributes ,也就是安全屬性,它是作為windows內核對象必須要有的參數類型。
????????第二個參數是指復位方式,如果為TRUE,則事件對象需要顯式調用ResetEvent函數來重置為無信號狀態;如果為FALSE,則事件對象在單個等待線程被釋放后自動重置為無信號狀態。
? ? ? ? 第三個參數為信號狀態也就是指定事件對象的初始狀態,如果為TRUE,則事件對象被創建時處于有信號狀態;如果為FALSE,則處于無信號狀態。
? ? ? ? 第四個參數為指定事件對象的名稱,通常為NULL,為無名的事件對象。
2控制事件狀態
????????事件對象有兩種狀態——發信號和不發信號。
???????SetEvent:將事件對象的狀態置為發信號狀態,允許等待該事件的線程繼續執行。
? ? ? ?ResetEvent:將事件對象的狀態置為不發信號狀態。
WINBASEAPI BOOL WINAPI SetEvent(_In_ HANDLE hEvent);WINBASEAPI BOOL WINAPI ResetEvent(_In_ HANDLE hEvent);
? ? ? ? 原型為上述代碼,參數都是為 HANDLE (句柄),也就是?CreateEven 函數的返回值,
3.等待事件對象:
? ? ??使用 WaitForSingleObject 或 WaitForMultipleObjects?函數等待一個或多個事件對象變為信號態, 線程才會繼續向下執行。
? ? ? ? 函數原型如下,參數都大致相同。
WINBASEAPI DWORD WINAPI WaitForSingleObject(_In_ HANDLE hHandle,_In_ DWORD dwMilliseconds);
四、實戰案例
? ? ? ? 上面我們已經講述了事件對象的作用以及一些常用的api方法和屬性,下面我將會通過一個實際有代表性的案例來繼續講解事件對象,來加深它的用法和印象。
1.案例描述?
? ? ? ? 下面游樂園有兩個售票口 A 和?售票口 B,游樂園限制最多100人進,假設這兩個售票口所賣的是不同種類的票,一共有100張。那么該如何設計程序,保證售票口 A 和?售票口 B 同時所賣的票不是同一張票。
2.代碼設計
? ? ? ? 上述案例我們可以用編程的角度去分析問題和解決問題。
????????售票口 A 和?售票口 B 可以分別看作兩個線程,線程 A和線程 B。100張票可以當作全局變量,作為線程A,B需要訪問的公共資源。代碼設計為:
#include <stdio.h>
#include <windows.h>
#include <process.h>// 共享資源 (100張票)
int iTickets = 100;// 事件對象
HANDLE g_hEvent;int main()
{// 線程A 和 線程BHANDLE hThreadA, hThreadB;hThreadA = CreateThread(NULL, 0, SellTicketA, NULL, 0, 0);hThreadB = CreateThread(NULL, 0, SellTicketB, NULL, 0, 0);CloseHandle(hThreadA); CloseHandle(hThreadB);system("pause");return 0;
}
????????那么就只需要保證線程A 和 線程 B在同一時間只能對共享資源的單一訪問,這里我們就可以用到事件對象(Event Objects)。
//手動重置 FALSE:設置無信號狀態,未觸發狀態g_hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);SetEvent(g_hEvent);Sleep(4000);CloseHandle(g_hEvent);
????????具體做法為 進程A 可以通過 SetEvent函數 將事件對象的狀態設置為有信號狀態,進程B 則可以通 WaitForSingleObject 等函數等待該事件對象變為有信號狀態,從而實現進程間的信號傳遞和協調,代碼為:
while (1){WaitForSingleObject(g_hEvent, INFINITE);if (iTickets > 0){Sleep(1);iTickets--;printf("A remain %d\n",iTickets);}else{break;}SetEvent(g_hEvent);}return 0;
?3.總設計代碼
? ? ? ? 以下是設計的總代碼:
#include <stdio.h>
#include <windows.h>
#include <process.h>// 共享資源 (100張票)
int iTickets = 100;// 事件對象
HANDLE g_hEvent;DWORD WINAPI SellTicketA(void* arg)
{while (1){WaitForSingleObject(g_hEvent, INFINITE);if (iTickets > 0){Sleep(1);iTickets--;printf("A remain %d\n",iTickets);}else{break;}SetEvent(g_hEvent);}return 0;
}
DWORD WINAPI SellTicketB(void* arg)
{while (1){WaitForSingleObject(g_hEvent, INFINITE);if (iTickets > 0){Sleep(1);iTickets--;printf("B remain %d\n", iTickets);}else{break;}SetEvent(g_hEvent);}return 0;
}int main()
{// 線程A 和 線程BHANDLE hThreadA, hThreadB;hThreadA = CreateThread(NULL, 0, SellTicketA, NULL, 0, 0);hThreadB = CreateThread(NULL, 0, SellTicketB, NULL, 0, 0);CloseHandle(hThreadA); CloseHandle(hThreadB);//手動重置 FALSE:設置無信號狀態,未觸發狀態g_hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);SetEvent(g_hEvent);Sleep(4000);CloseHandle(g_hEvent);system("pause");return 0;
}
4.運行結果
? ? ? ? 代碼運行的總結果如下。
?