关于事件
事件(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
原文链接: https://www.cnblogs.com/poissonnotes/p/7681484.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/261384
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!