概述
- 在用户模式下进行线程同步的最大好处就是速度非常快。
- 与用户模式下的同步机制相比,内核对象的用途要广泛的多。实际上,内核对象的唯一缺点就是性能。
- 对线程同步来说,内核对象的每一种要么处于触发状态,要么处于未触发状态。
Microsoft为每种对象创建了一些规则,规定如何在这两种状态之间进行转换。
例如:进程内核对象在创建的时候总是处于未触发状态。当进程终止的时候,操作系统会自动使进程内核对象变成触发状态。当进程内核对象被触发后,它将永远保持这种状态,再也不会回到未触发状态。线程内核对象也是这样。 - 下列内核对象既可以处于触发状态,也可以处于未触发状态:
- 进程
- 线程
- 作业
- 文件以及控制台的标准输入流、输出流、错误流
- 事件
- 可等待的计时器
- 信号量
- 互斥量
等待函数
WaitForSingleObject
1 |
DWORD WaitForSingleObject(HANDLE hObject, DOWRD dwMilliseconds); |
-
等待函数使一个线程自愿进入等待状态,直到指定的内核对象被触发为止。
当一个线程在调用一个等待函数的时候,发现指定的内核对象已经处于触发状态了,那它是不会进入等待状态的。- 第一个参数是要等待的内核对象
- 第二个参数是线程愿意花多长的时间等待这个内核对象被触发
-
如果第二个参数传INFINITE,意思是线程愿意一直等待,或者等到这个进程终止为止。
-
返回值
- 如果等待的对象被触发,返回值是WAIT_OBJECT_0
- 如果等待超时,返回值是WAIT_TIMEOUT
- 如果传入了一个无效句柄,返回值是WAIT_FAILED
WaitForMultipleObjects
1 2 3 4 |
DWORD WaitForMultipleObjects(DWORD dwCount, CONST HANDLE* phObjects, BOOL bWaitAll, DWORD dwMilliseconds); |
- 是原子操作。意味着当函数检查内核对象的状态时,其他线程不能在背后修改对象的状态。
- 第一个参数希望检查的内核对象的数量(1~64之间)
- 第二个参数指向一个内核对象句柄的数组
-
第三个参数是希望以哪种方式使用这个函数
- FALSE:让线程进入等待状态直到指定内核对象中的一个被触发为止
- TRUE:让线程进入等待状态直到指定内核对象中的全部被触发为止
- 第四个参数是等待的时间长度,如果时间到了还是没触发,那么函数也会返回。
- 这个函数的返回值能能看出继续运行的原因
- 如果等待超时,返回值是WAIT_TIMEOUT
- 如果传入了一个无效句柄,返回值是WAIT_FAILED
- 如果bWaitAll传的是TRUE,并且所有对象都被触发了,那么返回值是WAIT_OBJECT_0
- 如果bWaitAll传的是FALSE,只要任何对象被触发,函数就会立即返回,返回值是WAIT_OBJECT_0和(WAIT_OBJECT_0 + dwCount - 1)之间的任何一个值。
- 换句话说,如果返回值既不是WAIT_FAILED也不是WAIT_TIMEOUT,那么把返回值减去WAIT_OBJECT_0,得到的应该是第二个参数传给函数的句柄数组的一个索引。
等待成功所引起的副作用
- 对一些内核对象来说,成功地调用WaitForSingleObject或者WaitForMultipleObject,实际上会改变对象的状态。
一个成功的调用指的是函数发现对象已经被触发了,然后返回WAIT_OBJECT_0的一个相对值。 - 如果对象的状态发生了变化,则称之为等待成功所引起的副作用。
假设线程正在等待一个自动重置事件对象,当事件被触发时,函数会检测到这一情况,这时它可以直接返回WAIT_OBJECT_0给调用函数。但是,再函数返回之前,它会使事件变为非触发状态(所谓等待成功所引起的副作用)。 - 如第2条,自动重置事件之所以会有这样的副作用,原因是Microsoft为此类对象定义了这样的规则。
不同的对象有不同的副作用。
有些对象完全没有副作用,比如进程、线程。
事件内核对象
事件的内容
- 包含一个使用计数。
- 一个用来表示事件是自动重置事件还是手动重置事件的布尔值。
- 一个用来表示事件有没有被触发的布尔值。
关于触发
- 事件的触发表示一个操作已经完成。
- 当一个手动重置事件被触发的时候,正在等待改事件的所有线程都将变成可调度状态。
- 当一个自动重置事件被触发的时候,只有一个正在等待该事件的线程会变成可调度状态。
事件的用途
- 让一个线程执行初始化工作,然后再触发另一个线程,让它执行剩余的工作。
- 比如一开始将事件初始化为未触发状态,然后当线程完成初始化工作的时候,触发事件。
此时,另一个线程正在等待该事件,让它发现事件被触发后,就变为可调度状态。这时,该线程知道第一个线程已经完成了它的任务。
函数
1 2 3 4 |
HANDLE CreateEvent(PSECURITY_ATTRIBUTES psa, BOOL bManualReset, BOOL bInitialState, PCTSTR pszName); |
- 第二个参数表示手动重置还是自动重置
- 手动重置TRUE
- 自动重置FALSE
- 第三个参数表示事件的触发状态
- 触发状态TRUE
- 未触发状态FALSE
1 2 3 4 |
HANDLE CreateEventEx(PSECURITY_ATTRIBUTES psa, PCTSTR pszName DWORD dwFlags, DWORD dwDesireAccess); |
- 第三个参数表示事件的触发状态
- CREATE_EVENT_INITIAL_SET表示将事件初始化为触发状态,否则为未触发状态。
- CREATE_EVENT_MANUAL_RESET表示将创建的是一个手动重置事件,否则将创建的是一个自动重置事件。
- 第四个参数允许指定在创建事件时返回的句柄对事件有何种访问权
1 2 3 4 5 6 7 8 9 |
HANDLE OpenEvent(DWORD dwDesiredAccess, BOOL bInherit, PCTSTR pszName); // 把事件变为触发状态 HANDLE SetEvent(HADNLE hEvent); // 把事件变为未触发状态 BOOL ResetEvent(HANDLE hEvent); |
1 |
BOOL PulseEvent(HANDLE hEvent); |
- 这个函数会先触发事件然后立即将其恢复到未触发状态。(脉冲触发)
- 对一个手动重置事件调用,当事件被脉冲触发的时候,正在等待该事件的线程都会变成可调度状态。
- 对一个自动重置事件调用,只有一个正在等待该事件的线程会变成可调度状态。
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 关于异常的捕获和dump文件的生成07/05
- ♥ Windows物理内存虚拟内存03/28
- ♥ WindowsHOOK相关03/17
- ♥ Soui一03/17
- ♥ breakpad记述:Windows下静态库的编译使用03/15
- ♥ Windows 核心编程 _ 线程调度07/07