在之前的课程里面讲过,线程在进入临界区之前会调用WaitForSingleObject或者WaitForMultipleOobjects,此时如果有信号,线程会从函数中退出并进入临界区,如果没有信号那么线程将自己挂入等待链表,然后将自己挂入等待网,最后切换线程。
其他线程在适当的时候,调用方法修改被等待对象的 SignalState 为有信号(不同的等待对象,会调用不同的函数),并将等待该对象的其他线程从等待链表中摘掉,这样,当前线程便会在 WaitForSingleObject 或者 WaitForMultipleObjects 恢复执行(在哪切换在哪开始执行),如果符合唤醒条件,此时会修改 SignalState 的值,并将自己从等待网上摘下来,此时的线程才是真正的唤醒。
被等待对象不同,主要在2个点上会有差异:
创建事件对象:信号
测试代码:
HANDLE g_hEvent;
VOID WINAPI ThreadProc1(LPVOID text)
{
::WaitForSingleObject(g_hEvent, INFINITE);
printf("ThreadProc1函数执行...\n");
}
VOID WINAPI ThreadProc2(LPVOID text)
{
::WaitForSingleObject(g_hEvent, INFINITE);
printf("ThreadProc2函数执行...\n");
}
VOID WINAPI ThreadProc3(LPVOID text)
{
::WaitForSingleObject(g_hEvent, INFINITE);
printf("ThreadProc3函数执行...\n");
}
int main()
{
//默认安全属性 对象类型 初始状态 名字
g_hEvent = ::CreateEvent(NULL, false, FALSE, NULL);
HANDLE hThread[3];
//创建3个线程
hThread[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc1, NULL, 0, NULL);
hThread[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc2, NULL, 0, NULL);
hThread[2] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc3, NULL, 0, NULL);
//设置事件为已通知
SetEvent(g_hEvent);
//等待线程结束 销毁内核对象
WaitForMultipleObjects(3, hThread, TRUE, INFINITE);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
CloseHandle(hThread[2]);
CloseHandle(g_hEvent);
getchar();
return 0;
}
//参数1:默认安全属性
//参数2:对象类型
//参数3:初始化的事件状态
//参数4:名称
CreateEvent(NULL, TRUE, FALSE, NULL);
//如果参数2为true 通知类型对象,false 事件同步对象
//如果参数3为false未通知,true已通知
_DISPATCHER_HEADER
+0x000 Type //对应上面第2个参数
+0x001 Absolute
+0x002 Size
+0x003 Inserted
+0x004 SignalState //对应上面第3个参数
+0x008 WaitListHead
SetEvent函数分析
SetEvent对应的内核函数: KeSetEvent
- 修改信号值SignalState为1
- 判断对象类型
- 如果类型为通知类型对象(Type 0) 唤醒所有等待该状态的线程
- 如果类型为事件同步对象(Type 1) 从链表头找到第一个
还是这个伪代码
while(true)//每次线程被其他线程唤醒,都要进入这个循环
{
if(符合激活条件)//1超时 2等待对象SignalState > 0
{
//1修改SignalState
//2退出循环
}
else//SignalState不大于0 也没超时
{
if(第一次执行)
{
//将当前线程的等待块挂到等待对象的链表 (WaitListHead) 中;
//将自己挂入等待队列(KiaitListHead)
//切换线程...再次获得CPU时,从这里开始执行
}
}
}
1)线程将自己+5c位置清0
2)释放_KWAIT_BLOCK所占内存
如果事件类型为TRUE,跳到这