关于事件
事件(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