關於事件
事件(Event)是WIN32提供的最靈活的線程間同步方式,事件可以處於激發狀態(signaled or true)或未激發狀態(unsignal or false)。根據狀態變遷方式的不同,事件可分為兩類:
(1)手動設置:這種對象只可能用程序手動設置,在需要該事件或者事件發生時,采用SetEvent及ResetEvent來進行設置。
(2)自動恢復:一旦事件發生並被處理后,自動恢復到沒有事件狀態,不需要再次設置。
創建事件的函數原型為:
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
// SECURITY_ATTRIBUTES結構指針,可為NULL
BOOL bManualReset,
// 手動/自動
// TRUE:在WaitForSingleObject后必須手動調用ResetEvent清除信號
// FALSE:在WaitForSingleObject后,系統自動清除事件信號
BOOL bInitialState, //初始狀態
LPCTSTR lpName //事件的名稱
);
使用"事件"機制應注意以下事項:
(1)如果跨進程訪問事件,必須對事件命名,在對事件命名的時候,要注意不要與系統命名空間中的其它全局命名對象沖突;
(2)事件是否要自動恢復;
(3)事件的初始狀態設置。
看下面代碼:
1 // event.cpp : 定義控制台應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <wtypes.h> 6 #include <iostream> 7 using namespace std; 8 9 DWORD WINAPI ThreadProc(LPVOID lpParam); 10 DWORD WINAPI ThreadProc2(LPVOID lpParam); 11 12 DWORD g_dwThreadID; 13 DWORD g_dwThreadID2; 14 15 UINT g_nTickets = 300; //int g_nTickets = 300; //備注1 16 17 HANDLE g_hEvent1 = NULL; 18 HANDLE g_hEvent2 = NULL; 19 20 CRITICAL_SECTION g_cs; 21 22 int ThreadCout = 0; 23 24 int _tmain(int argc, _TCHAR* argv[]) 25 { 26 cout << "Main thread is running." << endl; 27 28 InitializeCriticalSection(&g_cs);//初始化臨界區 29 30 HANDLE hHandle = CreateThread(NULL, 0, ThreadProc, NULL, 0, &g_dwThreadID); 31 ThreadCout++; 32 HANDLE hHandle2 = CreateThread(NULL, 0, ThreadProc2, NULL, 0, &g_dwThreadID2); 33 ThreadCout++; 34 35 g_hEvent1 = CreateEvent(NULL, FALSE, TRUE, NULL); //備注5:g_hEvent1 = CreateEvent(NULL, TRUE, TRUE, NULL); 36 g_hEvent2 = CreateEvent(NULL, FALSE, TRUE, NULL); //備注5:g_hEvent2 = CreateEvent(NULL, TRUE, TRUE, NULL); 37 38 ResetEvent(g_hEvent1); 39 ResetEvent(g_hEvent2); 40 41 SetEvent(g_hEvent1); 42 43 44 while (TRUE) 45 { 46 EnterCriticalSection(&g_cs); 47 int nCount = ThreadCout; 48 LeaveCriticalSection(&g_cs); 49 50 if (nCount == 0) 51 { 52 cout << "Main thread is break." << endl; 53 break; 54 } 55 56 } 57 58 59 Sleep(1000); //備注4 60 61 CloseHandle(hHandle); 62 CloseHandle(hHandle2); 63 64 DeleteCriticalSection(&g_cs); 65 66 cout << "Main thread is end." << endl; 67 68 system("pause"); 69 return 0; 70 } 71 72 73 DWORD WINAPI ThreadProc(LPVOID lpParam) 74 { 75 // cout << "No." << g_dwThreadID << " thread is running." << endl; 76 while (TRUE) 77 { 78 WaitForSingleObject(g_hEvent1, INFINITE); 79 cout << "No.1 " << g_dwThreadID << " thread is running." << endl; 80 81 EnterCriticalSection(&g_cs); 82 int temp = g_nTickets; 83 LeaveCriticalSection(&g_cs); 84 85 cout << "No.1 " << g_dwThreadID << " thread is temp." << endl; 86 87 if (temp > 0) 88 { 89 Sleep(10); //Sleep(1000) //備注2 90 cout << "No.1-" << g_dwThreadID << " sell ticket : " << temp << endl; 91 92 93 EnterCriticalSection(&g_cs); 94 g_nTickets--; 95 LeaveCriticalSection(&g_cs); 96 97 SetEvent(g_hEvent2); 98 //ResetEvent(g_hEvent1);//備注6 99 } 100 else 101 { 102 cout << "No.1- break" << endl; 103 //ResetEvent(g_hEvent1);//備注6 104 SetEvent(g_hEvent2);//沒有這個ThreadProc2不能終止 //備注3 105 break; 106 } 107 } 108 109 EnterCriticalSection(&g_cs); 110 ThreadCout--; 111 LeaveCriticalSection(&g_cs); 112 cout << "No.1- end" << endl; 113 114 return 0; 115 } 116 117 DWORD WINAPI ThreadProc2(LPVOID lpParam) 118 { 119 // 120 while (TRUE) 121 { 122 WaitForSingleObject(g_hEvent2, INFINITE); 123 cout << "No.2 " << g_dwThreadID2 << " thread is running." << endl; 124 125 EnterCriticalSection(&g_cs); 126 int temp = g_nTickets; 127 LeaveCriticalSection(&g_cs); 128 129 if (temp > 0) 130 { 131 Sleep(10); //Sleep(1000) //備注2 132 cout << "No.2-" << g_dwThreadID2 << " sell ticket : " << temp << endl; 133 134 EnterCriticalSection(&g_cs); 135 g_nTickets--; 136 LeaveCriticalSection(&g_cs); 137 138 SetEvent(g_hEvent1); 139 //ResetEvent(g_hEvent2);//備注6 140 } 141 else 142 { 143 cout << "No.2- break" << endl; 144 //ResetEvent(g_hEvent2);//備注6 145 SetEvent(g_hEvent1);//同樣的問題,沒有這個ThreadProc不能終止 //備注3 146 break; 147 } 148 } 149 150 EnterCriticalSection(&g_cs); 151 ThreadCout--; 152 LeaveCriticalSection(&g_cs); 153 154 cout << "No.2- end" << endl; 155 return 0; 156 }
這個代碼是接上一遍關於UINT類型作為循環變量的不確定性問題繼續完善的,加入了臨界區控制全局變量的訪問。
本文要說明的是SetEvent和ResetEvent的使用,這個要看備注5和備注6。
備注5處:
CreateEvent的第二個參數決定了是否需要手動調用ResetEvent,當為TRUE時,是需要手動調用,如果不調用,會怎么樣呢?不調用,事件會處於一直有信號狀態,即備注6處。當為FALSE時候,不需要手動調用,調用不調用,效果一樣。把ResetEvent放在WaitForSingleObject前面也是很好的做法。
轉載請注明原創鏈接:http://blog.csdn.net/wujunokay/article/details/12272581