当使用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结构的内部成员为STATUS_PENDING,则该函数在操作完成之前不会返回。如果此参数为FALSE且操作仍处于挂起状态,则函数返回FALSE并且GetLastError函数返回ERROR_IO_INCOMPLETE。
对于我的代码,这意味着:
- 如果 I/O 快速完成,ReadFile返回true。
- 如果 I/O 仍然挂起,那么我等待它完成
- 如果还有其他错误,我会收到该错误。
这一切都很好。
大多数情况下,这一切都很好
我遇到了实际ReadFile
操作需要十分钟才能返回的问题。当它返回时,它返回:
The device is not ready (21)
这是这家供应商的存储系统的一个众所周知的问题。.
我想做的是使用 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
我可以使用超时模拟同步操作ReadFile
吗?GetOverlappedResultEx
我敢肯定我最终会得到一个涉及消息传递计时器的错误黑客(或者是线程计时器?或者是警报计时器?和事件?和我自己的事件?或者重叠的事件?)。但我宁愿使用好的解决方案而不是我的解决方案。