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中代码如下:

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来进行等待,但是用的是事件的对象, 然后最好要手动的设置事件的信号。

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秒(或者是某个操作完成),他必须是有

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

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

//创建事件对象
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注释掉,
这样就变成自动复位的了。

时间: 2024-10-11 14:31:59

VC++线程同步(四) 事件使用例子的相关文章

VC++线程同步(三) 临界区使用例子

临界区(Crtical Section)同步对象 用户模式下的同步对象 Win32中,最容易使用的一个同步机制就是(关键段)Critical Section, 某些共享资源具有互斥性,也就是它要求被互斥地使用,他也是用于资源的互斥, 在大部分情况下,使用临界区替换Mutex(Mutex是内核模式下的同步对象). 局限性:他只能用于同步单个进程中的线程. 在任何同步机制当中,无论是哪个操作系统下,都不要 长时间的锁住资源,如果一直锁定资源,就会一致阻止其他线程的执行, 使整个程序,处于完全停止的状

VC++线程同步(五) 信号量使用例子

信号量(Semaphore) 信号量是内核对象,使用几个形象的例子,进行描述. 1 假设有5个位置,而外面有很多人要进来,那么当5个位置被人占用了 后,其他人就必须排队等待,每个人使用时间不同,5个占用的位置,其中有两个完成了,那么,排队的人中,最前面的两个人进行可以使用,但是最多就是5个人同时能够使用,这就是信号量. 2 例如我们在服务器中创建了一个线程池,它由5个线程组成,也就意味着,最多同时处理5个请求,一旦超过5个,那么请求就放入缓冲中,当一个或多个请求(最多5个)完成后,那么从缓冲中拿

VC++深入详解——16章:线程同步,事件对象

这章介绍另外:事件对象和关键代码段. 进程相关函数: CreateEvent函数: 第一个参数:安全属性,默认的安全属性为NULL 第二个参数:复位方式, 人工设置为TRUE,自动设置为FALSE, 当为人工设置时,等待事件的线程时,需要resetevent函数来设置其为无型号状态. 第三个参数:初始状态:TRUE为有信号状态,FALSE为无信号状态. 第四个参数:对象名称,NULL为匿名名称. 创建或打开一个命名或匿名的事件对象(也属于内核对象) 返回:返回的是事件对象的句柄. SetEven

VC++ 线程同步 总结

注:所谓同步,并不是多个线程一起同时执行,而是他们协同步调,按预定的先后次序执行. 与线程相关的基本函数包括:CreateThread:创建线程CloseHandle:关闭线程句柄.注意,这只会使指定的线程句柄无效(减少该句柄的引用计数),启动句柄的检查操作,如果一个对象所关联的最后一个句柄被关闭了,那么这个对象会从系统中被删除.关闭句柄不会终止相关的线程. 线程是如何运行的呢?这又与你的CPU有关系了,如果你是一个单核CPU,那么系统会采用时间片轮询的方式运行每个线程:如果你是多核CPU,那么

线程同步wait notify小例子

<strong>线程同步小例子:开启两个线程实现拿鸡蛋 放鸡蛋交叉进行</strong> </pre><pre code_snippet_id="574008" snippet_file_name="blog_20150107_1_8603573" name="code" class="java">public class Dofunction { private int num

VC++线程同步(二) Mutex互斥量的例子

同步对象使用实例 Win32窗口的建立: 我们将要学习的使用,分别是:互斥量,临界区,事件,信号量.所以我们需要一个窗口,呈现四种四种同步对象状态. 首先创建一个Win32项目,不要选空项目; 我们需要四个小窗口,先找到注册主窗口的代码. ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style= CS_HREDRAW | CS_VREDRAW

线程同步之事件

我们可以使用lock.Mutex来保证共享资源被正确的操作,但当多个线程之间需要相互通信时,如线程A完成之后要告诉线程B,B在接着做, 这时我们应该怎么处理,那就要用到线程的事件..Net中提供了AutoResetEvent和ManualResetEvent两个类来处理. 当线程需要独占资源时,使用AutoResetEvent.WaitOne()来等待资源,如果AutoResetEvent为非终止状态,则线程阻塞,等待其它线程释放资源.其它线程 使用完资源,通过调用Set方法来释放资源,通知等待

C++线程同步之事件

题目要求:点击抢红包后,先将第一个编辑框的值设置为1000,然后创建三个线程,让右边的编辑框值依次设置为1000(用事件完成) // MutexExDlg.h : 头文件 // #pragma once // CMutexExDlg 对话框 class CMutexExDlg : public CDialogEx { // 构造 public: CMutexExDlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据 enum { IDD = IDD_MUTEX

MFC线程(三):线程同步事件(event)与互斥(mutex)

前面讲了临界区可以用来达到线程同步.而事件(event)与互斥(mutex)也同样可以做到. Win32 API中的线程事件 HANDLE hEvent = NULL; void MainTestFun{ hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); SetEvent(hEvent); char g_charArray[4]; CString szResult; //下面三个线程中的任意一个访问g_charArray的时候其他线程都不能访问 AfxBe