windows核心编程读书笔记7——内核态线程同步(2)利用内核对象同步

事件对象

event对象常用来多个线程间进行工作的同步,如线程A先执行一些初始化工作,触发evnet,通知线程B初始化工作已经完成,可以进行接下来的工作。

创建event对象

HANDLE WINAPI CreateEvent(
  _In_opt_  LPSECURITY_ATTRIBUTES lpEventAttributes,  // 设置安全属性
  _In_      BOOL bManualReset,                        // 是否人工重置状态(人工重置则不会自动改变事件状态, 自动重置则会自动将事件恢复为未触发)
  _In_      BOOL bInitialState,                       // 事件初始状态(触发/未触发)
  _In_opt_  LPCTSTR lpName                            // 事件名称
);

值得注意的是
<span style="color:#FF0000;">BOOL bManualReset, </span> 

 若 为人工重置,那么当事件触发时,所有等待线程均能够获得事件对象,且不会自动重置事件状态。若为自动重置,则仅有一个线程wait获得该事件,同时置事件为未触发状态。 

另外一点,其他线程若想获得该事件对象句柄,可以也调用CreateEvent函数,并传入事件名称。若该事件已经存在,则直接返回句柄,若未存在则会创建该事件并返回句柄。

注意,若事件已经存在,再调用CreateEvent只会获取其句柄,但该函数的其他参数会忽略

对于自动重置事件,若multiplewait函数为全部等待状态,则对于仅自动重置事件触发时,multiplewait函数会忽略该event,同时不会重置事件,只有当所有的等待对象都触发时,multiplewait才会获取自动重置事件并自动重置为未触发状态。

若想在创建事件时指定的可以访问事件的权限,可以用

HANDLE WINAPI CreateEventEx(
  _In_opt_  LPSECURITY_ATTRIBUTES lpEventAttributes,
  _In_opt_  LPCTSTR lpName,
  _In_      DWORD dwFlags,                             // 可以是两种flags的任意组合<strong>CREATE_EVENT_INITIAL_SET</strong>、<strong>CREATE_EVENT_MANUAL_RESET</strong>
  _In_      DWORD dwDesiredAccess                      // 设置事件权限
);

获取事件句柄函数

HANDLE WINAPI OpenEvent(
  _In_  DWORD dwDesiredAccess,
  _In_  BOOL bInheritHandle,
  _In_  LPCTSTR lpName
);

改变事件触发状态

设置事件为触发状态

BOOL WINAPI SetEvent(
  _In_  HANDLE hEvent
);

设置事件未触发状态

BOOL WINAPI ResetEvent(
  _In_  HANDLE hEvent
);

可等待计时器内核对象

可等待计时器对象会在一定时后或每间隔一段时间触发,可用在某个时间的操作。

创建或获取可等待计时器

HANDLE WINAPI CreateWaitableTimer(
  _In_opt_  LPSECURITY_ATTRIBUTES lpTimerAttributes,
  _In_      BOOL bManualReset,                     // 是否人工重置
  _In_opt_  LPCTSTR lpTimerName
);

获取可等待计时器句柄

HANDLE WINAPI OpenWaitableTimer(
  _In_  DWORD dwDesiredAccess,
  _In_  BOOL bInheritHandle,
  _In_  LPCTSTR lpTimerName
);

不像事件对象,可等待计时器创建后总是未触发的

需要调用函数 SetWaitableTimer

BOOL WINAPI SetWaitableTimer(
  _In_      HANDLE hTimer,                          // 计时等待对象
  _In_      const LARGE_INTEGER *pDueTime,          // 何时触发对象(用负值表示相对于调用SetWaitableTimer后的时间 100纳秒为单位)
  _In_      LONG lPeriod,                           // 触发后间隔的触发频率(0 表示仅触发一次)
  _In_opt_  PTIMERAPCROUTINE pfnCompletionRoutine,  // APC调用函数
  _In_opt_  LPVOID lpArgToCompletionRoutine,        // APC调用参数
  _In_      BOOL fResume                            // 在可挂起的计算机系统中,是否恢复计算机来使等待线程执行CPU时间。
                                                    // 若传入FALSE,则会触发对象,但等待线程不会执行,除非直到计算机系统重新执行
 );

取消计时等待对象的时间设置

该函数会取消一切的SetWaitableTimer的计时设置。
<strong>但是该函数不会更改timer对象的触发状态</strong>,若已经触发,则该对象仍会处于触发状态。
BOOL WINAPI CancelWaitableTimer(
  _In_  HANDLE hTimer
);

计时等待对象 VS 用户计时器(SetTimer)

1、内核对象,用户对象

2、用户计时器会产生WM_TIMER消息,该消息会被送到调用SetTimer线程或创建窗口线程,同一时间仅有一个线程得到通知。

计时等待对象可多个线程同时被通知。

信号量内核对象

信号能够灵活的限制可被激活的线程数目,并确保线程数目不会超过设定的最大值。

具体使用流程为:

1、创建信号量对象,并指定最大资源数目与当前可用数目(常为0)。

2、创建多个资源请求线程,因为当前可用数目为0,线程等待。

3、当符合某种条件时,调用ReleaseSemaphore函数释放资源,这时候可用资源数目递增。

4、可以资源数目不再为0,等待线程获得资源,同时可以资源数目递减。


windows系统会确保当前可用资源数目大于等于0,同时不会超过最大值。


创建(或获取)信号量对象

HANDLE WINAPI CreateSemaphore(
  _In_opt_  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
  _In_      LONG lInitialCount,      // 初始当前可用资源数目
  _In_      LONG lMaximumCount,      // 最大可用资源数目
  _In_opt_  LPCTSTR lpName
);

CreateSemaphoreEx


获取信号量对象句柄

HANDLE WINAPI OpenSemaphore(
  _In_  DWORD dwDesiredAccess,
  _In_  BOOL bInheritHandle,
  _In_  LPCTSTR lpName
);

递增信号量可用资源

BOOL WINAPI ReleaseSemaphore(
  _In_       HANDLE hSemaphore,
  _In_       LONG lReleaseCount,
  _Out_opt_  LPLONG lpPreviousCount
);

互斥量内核对象

互斥量内核对象用于确保资源被唯一的线程访问,即互斥访问。

创建(获取)互斥量对象

HANDLE WINAPI CreateMutex(
  _In_opt_  LPSECURITY_ATTRIBUTES lpMutexAttributes,
  _In_      BOOL bInitialOwner,
  _In_opt_  LPCTSTR lpName
);

互斥量内核对象 有 引用计数器,线程ID已经递归计数器组成

线程ID用来记录当前获取互斥量对象的线程ID,0表示没人获取,互斥量处于触发状态。一旦,有个线程wait到互斥量,其内核对象线程ID为该线程ID,同时内核对象变为未触发状态,其他线程只能继续等待。但对于已经获得互斥量线程,其仍可以等待成功,这时候内核对象会递增其递归计数器。

调用ReleaseMutex 释放互斥量。对于多次递归进入的互斥量,要相应的多次调用release函数。

注意,当线程在获取了互斥量对象,而在调用ReleaseMutex之前结束的话,会产生遗弃问题


关于内核态同步对象的一些事项

1、一般的,通过内核对象来进行同步,其获取的内核对象句柄都是具有所有权限的(访问,改变触发状态等),但是我们可以在Create内核对象时,通过扩展函数ex函数设置可访问权限,那么当访问该内核对象句柄时,仅能够进行指定的权限访问。

2、内核对象命名与多用户系统

我们有多种方法可以在多个进程空间访问同一个内核对象(继承,dumplicatehandle,命名的内核对象)。

在使用命名内核对象访问时,需要注意在多用户系统中内核对象名称的前缀。

MSDN原话是:

The name can have a "Global\" or "Local\" prefix to explicitly create the object in the global or session namespace.

即加上“Global\”前缀,可以在多个用户间通过名称访问该内核对象,而"Local\"前缀仅能够当前用户通过名称访问内核对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值