线程同步——内核对象(互斥、事件、信号量、可等待计时器)

三、内核模式下的线程同步

  • Windows系统中有多种机制可用于线程同步,它们一般都被称之为内核对象(并非全部),一般我们常用的有以下几种:
    • 互斥对象(Mutex)
    • 事件对象(Event)
    • 信号量(Semaphore)
    • 可等待计时器(Waitable Timer)
0.等待函数
  1. WaitForSingleObject
    等待函数的作用是使一个线程进入到等待状态,直到指定的内核对象被触发为止,其函数原型如下所示:
DWORD WaitForSingleObject(
    _In_    HANDLE    hHandle,                //内核对象句柄
    _In_    DWORD    dwMiliseconds        //等待超时时间(微秒,INFI)
);

/******
***return
WAIT_OBJECT_0: 成功返回
WAIT_TIMEOUT:  超时返回
WAIT_FAILED:   传入的参数错误
*/

在创建线程时使用等待函数后,此函数会在等待超时或线程结束时返回,因此我们的主线程一次只能启动一个线程,从而避免上述例子中多线程访问同一个数据时所引发的问题.

  1. WaitForMultipleObjects

与WaitForSingleObject类似,唯一的不同之处在于它允许调用线程同时检查多个内核对象的触发状态

DWORD WaitForMultipleObjects(
    DWORD dwCount,              // 检查的内核对象的数量
    CONST HANDLE* phObjects,    // 内核对象句柄数组
    BOOL bWaitAll,              // 是否在所有内核对象触发之后返回
    DWORD dwMilliseconds)       // 等待时间
/***********
* return
WAIT_FAILED
WAIT_TIMEOUT
// 如果 bWaitAll 是 TRUE ,则返回 WAIT_OBJECT_0
// 如果 bWaitAll 是 FALSE, 则返回值是 WAIT_OBJECT_0和(WAIT_OBJECT_0 + dwCount - 1) 之间的任意一个值,内核对象数组的下标为:返回值 - WAIT_OBJECT_0
*/

对一些内核对象来说,成功地调用 WaitForSingleObject 与 WaitForMultipleObjects 事实上会改变对象的状态。如:自动重置事件内核对象,当时间对象被触发的时候,函数会检测到这一情况,这时它可以直接返回 WAIT_OBJECT_0 给调用线程。但是,就在函数返回之前,它会使事件变为非触发状态——这就是等待成功所引起的副作用。 WaitForMultipleObjects是以原子方式工作的,当函数检查内核对象的状态时,任何其他线程都不能在背后修改对象的状态。

1.事件对象

事件(Event) 是在线程同步中最常使用的一种同步对象,而且比其他对象要简单一些。像其他对象一样,事件包含了一个使用计数,一个是用来标识自动重置/手动重置的BOOL值,另一个是表示事件有没有触发的BOOL值

  • 事件对象有两种状态,他们分别是:
    • 手动状态:被触发后所有等待该事件的线程都将变为可调度状态,常用于控制具有较强自定义要求的多线程同步环境.
    • 自动状态:被触发后只有一个等待事件线程会变成可调度状态。
/*关键函数*/
HANDLE CreateEvent(
    LPSECURITY_ATTRIBUTES  lpEventAttributes,//属性
    BOOL  hManualReset,    //手工重置
    BOOL  bInitialState,      //初始状态
    LPCTSTR  lpName            //事件对象名称
);
//注:非手工状态下,调用SetEvent放行一个线程后,会自动再次设为无
//信号状态,直到再次调用SetEvent

/*设置标记为有信号状态(释放等待函数)*/
BOOL SetEvent(
    HANDLE hEvent        //事件对象句柄
);

/*重置标记为无信号状态(阻塞等待函数)*/
BOOL    WINAPI    ResetEvent(
    HANDLE  hEvent        //事件对象句柄
);

//打开内核对象
HANDLE OpenEvent(
    DWORD    dwDesiredAccess,//对事件对象的请求访问权限
    BOOL        bInheritHandle,//是否能继承
    LPCTSTR    lpName        //事件对象的名字
);

一个防多开的例子

if (!OpenEvent(EVENT_MODIFY_STATE, TRUE,L"Global\\Text"))
        CreateEvent(NULL, TRUE, TRUE, L"Global\\Text");
else
        return 0;

示例:

int     g_nNum = 0;
HANDLE  g_hEventA = nullptr;
HANDLE  g_hEventB = nullptr;
DWORD WINAPI ThreadProcA(LPVOID lpParam) {
    for (int i = 0; i < 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值