WaitForSingleObjectEx超时参数说明

本文详细解析了WaitForSingleObjectEx函数的工作原理及其参数含义,通过具体示例演示了当存在完成例程或APC例程时,函数的行为表现,并提供了两个测试Demo来验证这些行为。

WaitForSingleObjectEx函数原型如下

DWORD WINAPI WaitForSingleObjectEx(
  __in  HANDLE hHandle,
  __in  DWORD dwMilliseconds,
  __in  BOOL bAlertable
);


在SDK6.1文档中,对参数dwMilliseconds说明如下:

The time-out interval, in milliseconds. The function returns if the interval elapses, even if the object's state is nonsignaled and no completion routines or APCs are queued.If dwMilliseconds is zero, the function tests the object's state and checks for queued completion routines or APCs and then returns immediately. IfdwMilliseconds is INFINITE, the function's time-out interval never elapses.

我对其中标红语句的理解是,如何超过时间间隔,即使(even if)对象信号未被触发并且没有完成例程或者APC例程排队。我觉得比较难翻译及理解。于是我又查了下网上MSDN,对参数dwMilliseconds说明如下:

The time-out interval, in milliseconds. If a nonzero value is specified, the function waits until the object is signaled, an I/O completion routine or APC is queued, or the interval elapses. IfdwMilliseconds is zero, the function does not enter a wait state if the criteria is not met; it always returns immediately. IfdwMilliseconds is INFINITE, the function will return only when the object is signaled or an I/O completion routine or APC is queued.

还是没有搞清楚若超过时间间隔,但是当前有完成例程或者APC例程,函数是否返回,于是写了个小Demo测试了一下

#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;

VOID NTAPI ApcFun(__in ULONG_PTR Parameter)
{
	cout<<"ApcFun Running."<<endl;
	Sleep(10000);
}

int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hEvent=CreateEvent(NULL,FALSE,FALSE,_T(""));

	DWORD dwRet=QueueUserAPC(ApcFun,GetCurrentThread(),NULL);
	cout<<dwRet<<endl;
	dwRet=QueueUserAPC(ApcFun,GetCurrentThread(),NULL);
	cout<<dwRet<<endl;
	dwRet=WaitForSingleObjectEx(hEvent,1000,TRUE);
	cout<<dwRet<<endl;

	CloseHandle(hEvent);
	return 0;
}


测试结果是即使超时时间已到,若当前存在完成例程或者APC例程,会把所有的例程执行完毕后才会返回。

如果WaitForSingleObjectEx第3个参数为TRUE,那么WaitForSingleObjectEx在执行时发现存在APC例程需要执行,则当前等待线程执行APC过程,执行完毕后,

不管同步对象是否有信号或者是否超时,函数返回,返回值为WAIT_IO_COMPLETION。

            我写个小demo供测试

#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;

HANDLE hEvent;

DWORD WINAPI ThreadFun(LPVOID lpThreadParameter)
{
	hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
	DWORD dwRet=WaitForSingleObjectEx(hEvent,INFINITE,TRUE);
	CloseHandle(hEvent);
	return 0;
}

VOID NTAPI UserApc(ULONG_PTR Parameter)
{
	for (int i=0;i<10000;i++)
	{
		for (int j=0;j<10000;j++)
		{
		}
		cout<<i<<endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hThread=CreateThread(NULL,NULL,ThreadFun,NULL,NULL,NULL);
	Sleep(500);
	DWORD dwRet=QueueUserAPC(UserApc,hThread,NULL);
	SetEvent(hEvent);
	WaitForSingleObject(hThread,INFINITE);
	return 0;
}


 

在主函数中Sleep(500)的意思是使子线程初始化完毕并且线程函数ThreadFun能够运行起来,若没有这一句,有很大的可能

QueueUserAPC执行完毕后子线程还在初始化中,此时若发现APC队列中存在APC例程,则会先执行apc队列,然后再执行

线程函数,这样会造成WaitForSingleObjectEx执行是apc队列是空的,达不到测试的目的,

就算APC执行过程中同步对象已然有信号,WaitForSingleObjectEx已然会返回WAIT_IO_COMPLETION

<think> 我们正在讨论Windows同步机制中关于`WaitForSingleObjectEx`和`WaitForSingleObject`的区别。根据用户的问题,他特别询问了必须使用`WaitForSingleObjectEx`替代`WaitForSingleObject`的场景。 关键点在于`WaitForSingleObjectEx`支持可提醒I/O(Asynchronous Procedure Calls, APC)的等待,而`WaitForSingleObject`不支持。因此,当需要处理APC时,必须使用`WaitForSingleObjectEx`。 具体场景包括: 1. **需要处理异步过程调用(APC)的情况**: - 当线程安排了APC,并且希望在这些APC被调用时能够唤醒等待,必须使用可提醒的等待函数。`WaitForSingleObjectEx`允许线程在等待期间处理APC,而`WaitForSingleObject`则不会。 2. **可提醒I/O操作**: - 在使用可提醒I/O(如通过`ReadFileEx`或`WriteFileEx`)时,I/O完成回调是通过APC执行的。为了在等待过程中处理这些回调,线程必须处于可提醒状态。因此,需要使用`WaitForSingleObjectEx`(或其它可提醒版本的等待函数)并设置`bAlertable`参数为`TRUE`。 3. **需要同时等待对象和APC的情况**: - 如果希望在等待一个对象(如互斥量、事件等)的同时,又能够处理已经排队的APC,那么必须使用`WaitForSingleObjectEx`。 4. **避免死锁或提高响应性**: - 在复杂的多线程应用中,有时需要及时处理APC以避免死锁或提高响应性。例如,一个线程可能在等待一个同步对象时,同时需要处理一些清理任务(通过APC),这时使用可提醒等待函数是必要的。 下面是一个代码示例,展示如何使用`WaitForSingleObjectEx`: ```c HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // 假设有APC被安排 // 可提醒等待,bAlertable设置为TRUE DWORD dwResult = WaitForSingleObjectEx(hEvent, INFINITE, TRUE); switch (dwResult) { case WAIT_OBJECT_0: // 事件已触发 break; case WAIT_IO_COMPLETION: // 一个APC被处理 break; case WAIT_FAILED: // 错误处理 break; } ``` 注意:`WaitForSingleObjectEx`的第三个参数`bAlertable`决定了是否允许在等待期间处理APC。如果设置为`TRUE`,则当APC被投递到当前线程时,等待会被中断并执行APC,然后函数返回`WAIT_IO_COMPLETION`。如果设置为`FALSE`,则不会处理APC,行为类似于`WaitForSingleObject`,但依然可以接收其他等待结果(如超时或对象触发)。 因此,必须使用`WaitForSingleObjectEx`的场景主要是当线程需要处理APC时。其他情况下,两者可以互换,但`WaitForSingleObject`更简单。 相关问题: 1. APC的执行机制是怎样的? 2. 如何避免在等待期间处理APC? 3. 在可提醒等待中,如何处理多个APC? </think> ### 必须使用 WaitForSingleObjectEx 替代 WaitForSingleObject 的场景 在 Windows 同步机制中,必须使用 `WaitForSingleObjectEx` 替代 `WaitForSingleObject` 的核心场景是 **需要处理异步过程调用 (APC) 的任务**。具体场景如下: #### 1. 可提醒 I/O 操作 当线程执行 **可提醒 I/O**(如 `ReadFileEx`/`WriteFileEx`)时,I/O 完成回调通过 APC 执行。此时必须使用可提醒等待函数: ```c ReadFileEx(hFile, buffer, size, &CompletionRoutine, NULL); // 必须使用可提醒等待 DWORD dwResult = WaitForSingleObjectEx(hEvent, INFINITE, TRUE); if (dwResult == WAIT_IO_COMPLETION) { // APC 回调已执行 } ``` 在此场景下: - 若用 `WaitForSingleObject`:I/O 回调将永远不会执行,可能导致数据丢失或死锁 - `WaitForSingleObjectEx` 的 `bAlertable=TRUE` 参数允许处理 APC 队列[^1] --- #### 2. 需要处理 APC 的定时等待 当线程需要在等待期间 **执行定时任务且处理 APC** 时: ```c QueueUserAPC(APC_Callback, hThread, NULL); // 同时等待信号和 APC DWORD dwResult = WaitForSingleObjectEx( hSemaphore, 5000, // 5 秒超时 TRUE // 允许处理 APC ); if (dwResult == WAIT_IO_COMPLETION) { // 定时器触发前已处理 APC } else if (dwResult == WAIT_TIMEOUT) { // 定时器超时 } ``` --- #### 3. 线程终止清理 当需要在线程退出时 **执行资源回收** 的 APC 回调: ```c // 注册线程终止回调 QueueUserAPC(CleanupAPC, GetCurrentThread(), (ULONG_PTR)resource); // 必须用可提醒等待接收终止信号 WaitForSingleObjectEx(hTerminateEvent, INFINITE, TRUE); ``` 此时 `WaitForSingleObject` 将阻塞清理函数的执行。 --- #### 4. 嵌套等待场景 当线程 **已在可提醒状态**(如通过 `SleepEx()`)时: ```c SleepEx(0, TRUE); // 进入可提醒状态 // 后续等待必须使用 Ex 版本 WaitForSingleObjectEx(hMutex, INFINITE, TRUE); ``` 否则会破坏现有 APC 处理机制[^1]。 --- ### 关键差异总结 | 特性 | `WaitForSingleObject` | `WaitForSingleObjectEx` | |---------------------------|------------------------|--------------------------| | APC 处理能力 | ❌ 不支持 | ✅ 支持 (`bAlertable=TRUE`) | | 可提醒状态要求 | 不要求 | 要求 (`bAlertable=TRUE` 时) | | 返回值 `WAIT_IO_COMPLETION` | 不会返回 | 可能返回 | | I/O 回调兼容性 | ❌ 不兼容 | ✅ 兼容 | > 决策树: > 当满足 $ (\text{使用 APC}) \lor (\text{可提醒 I/O}) \lor (\text{已在可提醒状态}) $ 时 > 必须使用 `WaitForSingleObjectEx` --- ### 相关问题 1. 如何避免 APC 导致的非预期唤醒? 2. 线程可提醒状态的具体实现原理是什么? 3. 在哪些场景下应避免使用 `WAIT_IO_COMPLETION`? [^1]: 当 I/O 完成时,回调函数并不会立即被调用,为了对线程 APC 队列中的内容项进行处理,线程必须将自己设置成可提醒状态。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值