VC++线程同步(四) 事件使用例子

简介:

事件(Event)同步对象


(内核级别)事件内核对象包含:

1 一个使用计数器

2 一个表示事件是否是自动重置还是手动重置的布尔值

3 一个表示事件有没有被触发的布尔值

4 当触发为true时,等待该事件的线程变为可调度状态

5 事件的触发表示一个操作已经完成


作用: 通知其他线程,我已经完成读写操作了,轮到你们来做了。


他分为两种类型:

1是手动重置事件,也就是要进行手动的触发和非触发状态的切换.

2是自动重置事件,这种情况下只需要设置触发事件,不用管什么时候切换触发状态。

尽量使用手动重置方式, 因为这种方式可控性强,不易出错.自动重置事件会引起成功等待的一些副作用.




相关的api:


1 CreateEvent函数

HANDLE CreateEvent(

LPSECURITY_ATTRIBUTES lpEventAttributes,//安全属性

BOOL bManualReset,  //复位方式

BOOL bInitialState,//初始状态

LPCTSTR lpName     //对象名称); 


返回一个Handle,事件同步对象的句柄。


参数1 lpEventAttributes    权限,一般NULL就是默认权限

参数2 bManualReset        TRUE代表手动重置,FALSE自动重置

参数3 bInitialState       TRUE代表可触发, FALSE非触发(阻塞)

参数3 lpName              一个对象的名称,跨进程寻址,一般NULL




2 SetEvent函数,设置事件对象为有信号状态

BOOL SetEvent( HANDLE hEvent);

hEvent 设置事件对象的句柄  就是CreateEvent返回的句柄.

当调用这个函数后,这个事件就是触发的状态。




3 ResetEvent 函数,设置事件对象为无信号,非触发

BOOL ResetEvent(HANDLE hEvent);

hEvent 设置事件对象的句柄  就是CreateEvent返回的句柄.

当调用这个函数后,这个事件就是非触发的状态。




                    使用例子


还是用之前的代码,就是一个小球碰到边界会反弹的程序.

我们需要三个线程,三个全局的事件对象句柄.

WndProc3中代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
case  WM_CREATE:
{
//系统中基于对话框字体的高度
int  cyChar = HIWORD(GetDialogBaseUnits());
thrParams3.hwnd = hWnd;
thrParams3.cyChar = cyChar;
//创建事件对象
g_hEvent1 = CreateEvent(NULL, TRUE,TRUE,NULL);   //手动复位  事件 有信号
g_hEvent2 = CreateEvent(NULL, TRUE, FALSE, NULL); //手动复位 事件 无信号
g_hEvent3 = CreateEvent(NULL, TRUE, FALSE, NULL); //手动复位 事件 无信号
// 创建三个线程
HANDLE  handleBall1 = CreateThread(NULL, 0, ThrBallProc1, &thrParams3, 0, NULL);
HANDLE  handleBall2 = CreateThread(NULL, 0, ThrBallProc2, &thrParams3, 0, NULL);
HANDLE  handleBall3 = CreateThread(NULL, 0, ThrBallProc3, &thrParams3, 0, NULL);
//关闭线程句柄
CloseHandle(handleBall1);
CloseHandle(handleBall2);
CloseHandle(handleBall3);
}


上面三个事件都是要手动复位的。

来看线程函数

前面依然使用WaitForSingleObject来进行等待,但是用的是事件的对象, 然后最好要手动的设置事件的信号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
DWORD  WINAPI ThrBallProc1( LPVOID  lp)
{
PPARAMS param3 =  static_cast <PPARAMS>(lp);
//休眠1秒
Sleep(1000);
//等待事件 使用INFINITE所以是无限等待 只有触发才返回
WaitForSingleObject(g_hEvent1, INFINITE);
//获取dc
HDC  hdc = GetDC(param3->hwnd);
//生成随机数种子
srand (GetTickCount());
//创建笔和画刷
HPEN  white_pen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
HBRUSH  green_brush = CreateSolidBrush(RGB(0,255,0));   //绿色的小球
HBRUSH  white_brush = CreateSolidBrush(RGB(255, 255, 255));
//小球的开始位置
int  ball_x = param3->cxClient / 2;
int  ball_y = param3->cyClient / 2;
//速度
int  xv = -4 +  rand () % 8;
int  yv = -4 +  rand () % 8;
DWORD  dwCurTime = GetTickCount();
while  (1)
{
//首先选择白色笔和白色画刷 设置上下文中去
SelectObject(hdc, white_pen);
SelectObject(hdc, white_brush);
// 绘制圆形小球
Ellipse(hdc, ball_x, ball_y, ball_x + 32, ball_y + 32);
//移动小球
ball_x += xv;
ball_y += yv;
//如果x轴  碰到边界 那就往反方向走
if  (ball_x <0 || ball_x > param3->cxClient - 32)
{
xv = -xv;
ball_x += xv;
}
else   // 或者是Y轴
{
if  (ball_y <17 || ball_y > param3->cyClient - 32)
{
yv = -yv;
ball_y += yv;
}
}
SelectObject(hdc, white_pen);
SelectObject(hdc, green_brush);
//画小球
Ellipse(hdc, ball_x, ball_y, ball_x + 32, ball_y + 32);
DWORD  dwTime = GetTickCount() - dwCurTime;   //当前时间 和第一次运行的时间差 
Sleep(10);
//判断现在的时间 减去初始化时间(循环外的那个时间) 是不是大于10秒钟
if  ((GetTickCount() - dwCurTime) > 1000 * 10)
{
//完成了当前线程操作
break ;
}
}
//线程的工作完成
//删除GDI对象
DeleteObject(white_brush);
DeleteObject(green_brush);
DeleteObject(white_pen);
ReleaseDC(param3->hwnd,hdc );
//设置窗口无效,并更新窗口
InvalidateRect(param3->hwnd,NULL,TRUE);
UpdateWindow(param3->hwnd);
// 手动设置事件信号
//让1号 事件对象  无信号  让2号事件  有信号
ResetEvent(g_hEvent1);
SetEvent(g_hEvent2);
return  0;
}



我们看到,三个线程都有一个绘制小球,但是他门没有同步出现,
而是第一个小球操作完10秒(或者是某个操作完成),他必须是有

一个对象,手动的设置触发,那这个等待函数对应的就会返回。


wKioL1l4APywzfhjAAAZWa2JiHM658.png-wh_50

wKiom1l4AXPBdlWdAAAhPg8rV5E010.png-wh_50




如果使用自动重置事件呢?


1
2
3
4
//创建事件对象
g_hEvent1 = CreateEvent(NULL, FALSE,TRUE,NULL);   //改成自动复位
g_hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
g_hEvent3 = CreateEvent(NULL, FALSE, FALSE, NULL);


然后在这个等待函数当作

WaitForSingleObject,当等待到了一个Event事件,他是触发状态,

他会判断他是不是自动重置事件的,如果是自动重置事件的话,

他会立即调用ResetEvent将这个事件设置成,非触发状态.

这种情况下, 最后可以不掉用这个函数。

因为他在等待函数中,以及调用了这个函数.

然后将这个ResetEvent注释掉,
这样就变成自动复位的了。


wKioL1l4BXmDy-CaAAAljmcse4s287.png-wh_50






 本文转自超级极客51CTO博客,原文链接:http://blog.51cto.com/12158490/1951050,如需转载请自行联系原作者




相关文章
|
28天前
|
存储 开发框架 开发者
QT C++焦点事件:多角度解析实用技巧与方法
QT C++焦点事件:多角度解析实用技巧与方法
127 0
|
5月前
|
存储 Cloud Native 程序员
C++ Qt 事件(event)
C++ Qt 事件(event)
|
28天前
|
测试技术 数据库 C++
Qt C++拖放事件探索之旅:多方法深入解析
Qt C++拖放事件探索之旅:多方法深入解析
114 1
|
C++
C++ 事件(event)使用总结
C++ 事件(event)使用总结
1196 0
C++ 事件(event)使用总结
|
安全
VC++关于使用WaitForSingleObject等待线程安全退出,出现死机问题的解决
VC++关于使用WaitForSingleObject等待线程安全退出,出现死机问题的解决
483 0
|
程序员 API 数据库

热门文章

最新文章