【发布时间】:2020-04-20 16:20:20
【问题描述】:
当使用GetOverlapedResult获取重叠(即异步)I/O操作的结果时,可以要求GetOverlappdResult“等待”:
DWORD le = ERROR_SUCCESS; //lastError = 0
if (!ReadFile(FSourceDiskHandle, buffer, BUFFER_SIZE, out bytesRead, overlapped)
{
//The read operation did not complete synchronously. See if it's still pending.
le = GetLastError;
if (le == ERROR_IO_PENDING)
{
le = ERROR_SUCCESS;
if (!GetOverlappedResult(FSourceDiskHandle, overlapped, out bytesRead, true) // <---bWait = true
le = GetLastError;
}
if (le != ERROR_SUCCESS)
LogFmt("Error reading source: %s (%d)', SysErrorMessage(le), le], TRACE_LEVEL_ERROR);
}
这里要注意的部分是GetOverlappedResult的最后一个参数:bWait:
bWait如果这个参数是TRUE,并且lpOverlapped结构的Internal成员是STATUS_PENDING,函数在操作完成之前不会返回。如果此参数为 FALSE 且操作仍处于挂起状态,则函数返回 FALSE,GetLastError 函数返回 ERROR_IO_INCOMPLETE。
对于我的代码,这意味着:
- 如果 I/O 快速完成,ReadFile 返回 true。
- 如果 I/O 仍然待处理,那么我等待它完成
- 如果还有其他错误,我会收到该错误。
这一切都很好。
大多数情况下,这一切都很好
我遇到了一个问题,实际的 ReadFile 操作需要 10 分钟 才能返回。当它返回时,它会返回:
The device is not ready (21)
这是one vendor's storage system. 的一个众所周知的问题。
我想做的是使用 Windows 的 异步 功能来等待 - 但有一个超时。
我注意到GetOverlappedResultEx,它有一种超时参数:
BOOL GetOverlappedResultEx(
HANDLE hFile,
LPOVERLAPPED lpOverlapped,
LPDWORD lpNumberOfBytesTransferred,
DWORD dwMilliseconds, <----------
BOOL bAlertable
);
但是当我查看文档时,这是我了解我不理解的 Windows 编程细节的地方 - 排队的 APC,可变等待。但它仍然听起来像我想要的::
dwMilliseconds超时间隔,以毫秒为单位。
如果 dwMilliseconds 为零并且操作仍在进行中,则 函数立即返回,GetLastError 函数返回 ERROR_IO_INCOMPLETE。
如果 dwMilliseconds 为非零且操作仍在进行中, 函数等待直到对象发出信号,I/O 完成 例程或 APC 已排队,或者在返回之前经过了间隔。 使用 GetLastError 获取扩展错误信息。
如果 dwMilliseconds 为 INFINITE,则函数仅在 对象已发出信号或 I/O 完成例程或 APC 已排队。
所以我尝试改变我的功能:
DWORD le = ERROR_SUCCESS; //lastError = 0
if (!ReadFile(FSourceDiskHandle, buffer, BUFFER_SIZE, out bytesRead, overlapped)
{
//The read operation did not complete synchronously. See if it's still pending.
le = GetLastError;
if (le == ERROR_IO_PENDING)
{
le = ERROR_SUCCESS;
//if (!GetOverlappedResult(FSourceDiskHandle, overlapped, out bytesRead, true) // <---bWait = true
if (!GetOverlappedResultEx(FSourceDiskHandle, overlapped, out bytesRead, 5000, False) //wait 5000 ms
le = GetLastError;
}
if (le != ERROR_SUCCESS)
LogFmt("Error reading source: %s (%d)', SysErrorMessage(le), le], TRACE_LEVEL_ERROR);
}
除了对 GetOverlappedResultEx 的调用不会在 5 秒(5,000 毫秒)内返回。相反,存储子系统需要 10 到 20 分钟才能恢复故障。
所以我随机尝试一下
我看到 GetOverlappedResultsEx 的另一个参数:
`bAlertable`
If this parameter is **TRUE** and the calling thread is in the waiting state, the function returns when the system queues an I/O completion routine or APC. The calling thread then runs the routine or function. Otherwise, the function does not return, and the completion routine or APC function is not executed.
A completion routine is queued when the [ReadFileEx][5] or [WriteFileEx][5] function in which it was specified has completed. The function returns and the completion routine is called only if *bAlertable* is **TRUE**, and the calling thread is the thread that initiated the read or write operation. An APC is queued when you call [QueueUserAPC][5].
这不像我的情况听起来:
- 我没有使用
ReadFileEx - 我没有打电话给
QueueUserAPC
但这并不一定会阻止我随意尝试并希望它们能奏效:
DWORD le = ERROR_SUCCESS; //lastError = 0
if (!ReadFile(FSourceDiskHandle, buffer, BUFFER_SIZE, out bytesRead, overlapped)
{
//The read operation did not complete synchronously. See if it's still pending.
le = GetLastError;
if (le == ERROR_IO_PENDING)
{
le = ERROR_SUCCESS;
//if (!GetOverlappedResult(FSourceDiskHandle, overlapped, out bytesRead, true) // <---bWait = true
if (!GetOverlappedResultEx(FSourceDiskHandle, overlapped, out bytesRead, 5000, False) //wait 5000 ms
if (!GetOverlappedResultEx(FSourceDiskHandle, overlapped, out bytesRead, 5000, True) //wait 5000 ms, alertable
le = GetLastError;
}
if (le != ERROR_SUCCESS)
LogFmt("Error reading source: %s (%d)', SysErrorMessage(le), le], TRACE_LEVEL_ERROR);
}
但它不起作用。
所以我问 Stackoverflow
我可以使用GetOverlappedResultEx 模拟同步ReadFile 操作但有 超时吗?
我敢肯定,我最终会得到一个涉及消息传递计时器(或者是线程计时器?或者是警报计时器?和事件?和我自己的事件?或者重叠中的事件?)。但我宁愿使用 good 解决方案,而不是 my 解决方案。
【问题讨论】:
-
WaitForSingleObject(overlapped.hEvent, 5000)? -
如果您愿意将自己限制在 C++20,您可以使用协程来做到这一点。 Asynchronous timeouts made easy 解释了如何使用预建的
wait_for功能。该实现基本上按照 Jonathan Potter 的建议进行。 -
@JonathanPotter - 但
GetOverlappedResultEx并做内部WaitForSingleObjectEx(overlapped.hEvent, dwMilliseconds, bAlertable) -
除了对
GetOverlappedResultEx的调用不会在 5 秒(5,000 毫秒)内返回。相反,存储子系统需要 10-20 分钟才能返回故障。 - 这不可能。你在这里犯了一些错误 -
GetOverlappedResultEx(FSourceDiskHandle, overlapped, out bytesRead, 5000, False)不会迟于 5 秒后返回,如果您说 而是继续需要 10-20 分钟 - 这是您的电话 @987654348 中的 100% 错误@ - 此 api 将仅等待来自重叠且独立于文件的事件。所以不能超过你的等待超时
标签: winapi overlapped-io getoverlappedresult