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,事件同步对象的句柄。
参数1lpEventAttributes 权限,一般NULL就是默认权限
参数2bManualReset TRUE代表手动重置,FALSE自动重置
参数3bInitialState TRUE代表可触发, FALSE非触发(阻塞)
参数3lpName 一个对象的名称,跨进程寻址,一般NULL
2 SetEvent函数,设置事件对象为有信号状态
BOOL SetEvent( HANDLE hEvent);
hEvent 设置事件对象的句柄 就是CreateEvent返回的句柄.
当调用这个函数后,这个事件就是触发的状态。
3 ResetEvent 函数,设置事件对象为无信号,非触发
BOOL ResetEvent(HANDLE hEvent);
hEvent 设置事件对象的句柄 就是CreateEvent返回的句柄.
当调用这个函数后,这个事件就是非触发的状态。
使用例子
还是用之前的代码,就是一个小球碰到边界会反弹的程序.
我们需要三个线程,三个全局的事件对象句柄.
WndProc3中代码如下:
caseWM_CREATE:{//系统中基于对话框字体的高度intcyChar=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);//手动复位事件无信号//创建三个线程HANDLEhandleBall1=CreateThread(NULL,0,ThrBallProc1,&thrParams3,0,NULL);HANDLEhandleBall2=CreateThread(NULL,0,ThrBallProc2,&thrParams3,0,NULL);HANDLEhandleBall3=CreateThread(NULL,0,ThrBallProc3,&thrParams3,0,NULL);//关闭线程句柄CloseHandle(handleBall1);CloseHandle(handleBall2);CloseHandle(handleBall3);}
上面三个事件都是要手动复位的。
来看线程函数
前面依然使用WaitForSingleObject来进行等待,但是用的是事件的对象, 然后最好要手动的设置事件的信号。
DWORDWINAPIThrBallProc1(LPVOIDlp){PPARAMSparam3=static_cast<PPARAMS>(lp);//休眠1秒Sleep(1000);//等待事件使用INFINITE所以是无限等待只有触发才返回WaitForSingleObject(g_hEvent1,INFINITE);//获取dcHDChdc=GetDC(param3->hwnd);//生成随机数种子srand(GetTickCount());//创建笔和画刷HPENwhite_pen=CreatePen(PS_SOLID,1,RGB(255,255,255));HBRUSHgreen_brush=CreateSolidBrush(RGB(0,255,0));//绿色的小球HBRUSHwhite_brush=CreateSolidBrush(RGB(255,255,255));//小球的开始位置intball_x=param3->cxClient/2;intball_y=param3->cyClient/2;//速度intxv=-4+rand()%8;intyv=-4+rand()%8;DWORDdwCurTime=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);DWORDdwTime=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);return0;}
我们看到,三个线程都有一个绘制小球,但是他门没有同步出现,
而是第一个小球操作完10秒(或者是某个操作完成),他必须是有
一个对象,手动的设置触发,那这个等待函数对应的就会返回。
如果使用自动重置事件呢?
//创建事件对象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注释掉,
这样就变成自动复位的了。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。