windows多线程同步--事件

简介:

事件是内核对象,多用于线程间通信,可以跨进程同步

事件主要用到三个函数:CreateEvent,OpenEvent,SetEvent,ResetEvent                                                        本文地址

 

CreateEvent

函数功能:创建事件

函数原型:

HANDLECreateEvent(

LPSECURITY_ATTRIBUTESlpEventAttributes,

BOOLbManualReset,

BOOLbInitialState,

LPCTSTRlpName

);

第一个参数:表示安全控制,一般直接传入NULL。

第二个参数:确定事件是手动置位还是自动置位,传入TRUE表示手动置位,传入FALSE表示自动置位。如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。打个小小比方,手动置位事件相当于教室门,教室门一旦打开(被触发),所以有人都可以进入直到老师去关上教室门(事件变成未触发)。自动置位事件就相当于医院里拍X光的房间门,门打开后只能进入一个人,这个人进去后会将门关上,其它人不能进入除非门重新被打开(事件重新被触发)。

第三个参数:表示事件的初始状态,传入TRUR表示已触发。

第四个参数:表示事件的名称,传入NULL表示匿名事件。

返回值:事件的句柄

 

OpenEvent

函数功能:根据名称获得一个事件句柄。

函数原型:

HANDLEOpenEvent(

DWORDdwDesiredAccess,

BOOLbInheritHandle,

LPCTSTRlpName     //名称

);

函数说明:

第一个参数:表示访问权限,对事件一般传入EVENT_ALL_ACCESS。详细解释可以查看MSDN文档。

第二个参数:表示事件句柄继承性,一般传入TRUE即可。

第三个参数:表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个事件。

返回值:返回事件的句柄

 

SetEvent

函数功能:触发事件

函数原型:BOOLSetEvent(HANDLEhEvent);

函数说明:每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态。

 

ResetEvent

函数功能:将事件设为末触发

函数原型:BOOLResetEvent(HANDLEhEvent);

 

下面从一个例子说明:假设有三个线程都需要使用打印机,我们可以使用互斥量来控制,这样就可以保证每次只有一个线程在使用打印机

使用自动置位,那么在调用WaitForSingleObject()后会自动调用ResetEvent()使事件变为未触发状态,为了使后面的线程能够继续打印,需要在线程函数的结尾调用SetEvent来触发事件

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
#include<string>
  #include<iostream>
  #include<process.h>
  #include<windows.h>
  using  namespace  std;
 
  //声明事件句柄
  HANDLE  hev;
 
//线程绑定的函数返回值和参数是确定的,而且一定要__stdcall
unsigned __stdcall threadFun( void  *param)
{
     WaitForSingleObject(hev, INFINITE); //等待事件被触发
     for ( int  i = 0; i < 10; i++)
         cout<<*(string *)(param)<< " " ;
     cout<<endl;
     SetEvent(hev); //设置事件为触发状态,使后面的线程可以打印
     return  1;
}
 
 
int  main()
{
     //创建一个未被触发的事件,事件是自动置位的
     hev = CreateEvent(NULL, FALSE, FALSE, NULL);
 
     SetEvent(hev); // 触发事件,使线程可以打印
 
     HANDLE  hth1, hth2, hth3;
     string s1 = "first" , s2 = "second" , s3 = "third" ;
 
     //创建线程
     hth1 = ( HANDLE )_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL);
     hth2 = ( HANDLE )_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL);
     hth3 = ( HANDLE )_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL);
 
     //等待子线程结束
     WaitForSingleObject(hth1, INFINITE);
     WaitForSingleObject(hth2, INFINITE);
     WaitForSingleObject(hth3, INFINITE);
 
     //一定要记得关闭线程句柄
     CloseHandle(hth1);
     CloseHandle(hth2);
     CloseHandle(hth3);
 
     //千万别忘了删除事件
     CloseHandle(hev);
}

 

image

 

使用手动置位,调用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
#include<string>
  #include<iostream>
  #include<process.h>
  #include<windows.h>
  using  namespace  std;
 
  //声明事件句柄
  HANDLE  hev;
 
//线程绑定的函数返回值和参数是确定的,而且一定要__stdcall
unsigned __stdcall threadFun( void  *param)
{
     WaitForSingleObject(hev, INFINITE); //等待事件被触发
     for ( int  i = 0; i < 10; i++)
         cout<<*(string *)(param)<< " " ;
     cout<<endl;
     //SetEvent(hev);//设置事件为触发状态,使后面的线程可以打印
     return  1;
}
 
 
int  main()
{
     //创建一个未被触发的事件,事件是手动置位的
     hev = CreateEvent(NULL, TRUE, FALSE, NULL);
 
     SetEvent(hev); // 触发事件,使线程可以打印
 
     HANDLE  hth1, hth2, hth3;
     string s1 = "first" , s2 = "second" , s3 = "third" ;
 
     //创建线程
     hth1 = ( HANDLE )_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL);
     hth2 = ( HANDLE )_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL);
     hth3 = ( HANDLE )_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL);
 
     //等待子线程结束
     WaitForSingleObject(hth1, INFINITE);
     WaitForSingleObject(hth2, INFINITE);
     WaitForSingleObject(hth3, INFINITE);
 
     //一定要记得关闭线程句柄
     CloseHandle(hth1);
     CloseHandle(hth2);
     CloseHandle(hth3);
 
     //千万别忘了删除事件
     CloseHandle(hev);
}

 

image

 

再通过下面的例子来看看时间有没有所有权属性:编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推

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
#include<string>
  #include<iostream>
  #include<process.h>
  #include<windows.h>
  using  namespace  std;
  //声明3个事件句柄
HANDLE   hev1, hev2, hev3;
 
//线程绑定的函数返回值和参数是确定的,而且一定要__stdcall
unsigned __stdcall threadFunA( void  *)
{
     for ( int  i = 0; i < 10; i++){
         WaitForSingleObject(hev1, INFINITE); //等待事件1
         cout<< "A" ;
         SetEvent(hev2); //触发事件2
     }
     return  1;
}
unsigned __stdcall threadFunB( void  *)
{
     for ( int  i = 0; i < 10; i++){
         WaitForSingleObject(hev2, INFINITE); //等待事件2
         cout<< "B" ;
         SetEvent(hev3); //触发事件3
     }
     return  2;
}
unsigned __stdcall threadFunC( void  *)
{
     for ( int  i = 0; i < 10; i++){
         WaitForSingleObject(hev3, INFINITE); //等待事件3
         cout<< "C" ;
         SetEvent(hev1); //触发事件1
     }
     return  3;
}
 
 
int  main()
{
     hev1 = CreateEvent(NULL, FALSE, FALSE, NULL);
     hev2 = CreateEvent(NULL, FALSE, FALSE, NULL);
     hev3 = CreateEvent(NULL, FALSE, FALSE, NULL);
     SetEvent(hev1); //触发事件1,从A开始打印
 
     HANDLE  hth1, hth2, hth3;
     //创建线程
     hth1 = ( HANDLE )_beginthreadex(NULL, 0, threadFunA, NULL, 0, NULL);
     hth2 = ( HANDLE )_beginthreadex(NULL, 0, threadFunB, NULL, 0, NULL);
     hth3 = ( HANDLE )_beginthreadex(NULL, 0, threadFunC, NULL, 0, NULL);
 
     //等待子线程结束
     WaitForSingleObject(hth1, INFINITE);
     WaitForSingleObject(hth2, INFINITE);
     WaitForSingleObject(hth3, INFINITE);
 
     //一定要记得关闭线程句柄
     CloseHandle(hth1);
     CloseHandle(hth2);
     CloseHandle(hth3);
 
     //删除事件
     CloseHandle(hev1);
     CloseHandle(hev2);
     CloseHandle(hev3);
}

 

image

 

由结果可知事件不具有所有权属性,即某个线程获取事件后,一定要等待事件再次被触发。可参考本博客其他文章中临界区、互斥量、信号量的所有权属性来理解。

 






本文转自tenos博客园博客,原文链接:http://www.cnblogs.com/TenosDoIt/p/3601458.html,如需转载请自行联系原作者

目录
相关文章
|
30天前
|
Java 云计算
Java多线程编程中的同步与互斥机制探析
在当今软件开发领域,多线程编程是一项至关重要的技能。本文将深入探讨Java中的同步与互斥机制,分析其在多线程环境下的应用及实现原理,帮助读者更好地理解并运用这一关键技术。
22 4
|
1月前
|
Python
在Python中,如何保证多个线程之间的同步?
在Python中,如何保证多个线程之间的同步?
22 4
|
1月前
|
Python
如何在Python中实现线程之间的同步和通信?
【2月更文挑战第17天】【2月更文挑战第51篇】如何在Python中实现线程之间的同步和通信?
|
1月前
|
算法 调度 索引
什么是多任务和线程?用线程写的一个udp同步聊天器
什么是多任务和线程?用线程写的一个udp同步聊天器
30 0
|
9天前
|
调度 Windows
|
25天前
|
存储 编解码 算法
【ffmpeg音视频同步】解决ffmpeg音视频中多线程之间的数据同步问题
【ffmpeg音视频同步】解决ffmpeg音视频中多线程之间的数据同步问题
38 2
|
30天前
|
安全 C++ 开发者
【C++多线程同步】C++多线程同步和互斥的关键:std::mutex和相关类的全面使用教程与深度解析
【C++多线程同步】C++多线程同步和互斥的关键:std::mutex和相关类的全面使用教程与深度解析
18 0
|
1月前
|
安全 Java 程序员
Java中的并发编程:掌握同步机制与线程安全
【2月更文挑战第12天】 在现代软件开发领域,多线程和并发编程已成为提高应用性能和资源利用率的关键技术。Java语言提供了丰富的并发编程工具和框架,使得开发高效、可靠的并发应用成为可能。本文将深入探讨Java中的并发编程,着重介绍同步机制、线程安全概念及其在实际开发中的应用。通过对这些知识点的深入分析和案例演示,旨在帮助读者建立起对Java并发编程的全面理解,从而更好地设计和实现线程安全的高并发系统。
|
2月前
|
并行计算 Java API
深入理解Java多线程编程:创建、状态管理、同步与通信
深入理解Java多线程编程:创建、状态管理、同步与通信

热门文章

最新文章