Winapi 在GetOverlappedResultEx中使用超时来模拟超时等待?

Winapi 在GetOverlappedResultEx中使用超时来模拟超时等待?,winapi,overlapped-io,getoverlappedresult,Winapi,Overlapped Io,Getoverlappedresult,使用获取重叠(即异步)i/O操作的结果时,可以要求GetOverlappdResult进行“等待”: 这是一个众所周知的问题 我想做的是使用Windows的异步功能来等待,但需要超时 我注意到,它有一个超时参数: BOOL GetOverlappedResultEx( HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, DWORD dwMill

使用获取重叠(即异步)i/O操作的结果时,可以要求GetOverlappdResult进行“等待”:

这是一个众所周知的问题

我想做的是使用Windows的异步功能来等待,但需要超时

我注意到,它有一个超时参数:

BOOL GetOverlappedResultEx(
  HANDLE       hFile,
  LPOVERLAPPED lpOverlapped,
  LPDWORD      lpNumberOfBytesTransferred,
  DWORD        dwMilliseconds, <----------
  BOOL         bAlertable
);
除了对GetOverlappedResultEx的调用在5秒(5000毫秒)内没有返回之外。相反,存储子系统继续需要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);
}
DWORD le=ERROR\u SUCCESS//lastError=0
if(!ReadFile(FSourceDiskHandle,buffer,buffer\u SIZE,out bytesRead,overlapped)
{
//读取操作未同步完成。请查看是否仍挂起。
le=GetLastError;
如果(le==错误\u IO\u挂起)
{
le=错误\成功;
//如果(!GetOverlappedResult(FSourceDiskHandle,overlapped,out bytesRead,true)//
我是否可以模拟同步
ReadFile
操作,但有一个超时, 使用
GetOverlappedResultEx

是的,您可以,完全像您一样,并且已经尝试过了。这不是模拟。这将完全是同步文件读取。因为同步读取-这是异步读取+在I/O完成时原地等待。所以代码可以是下一个:

ULONG ReadFileTimeout(HANDLE hFile, 
               PVOID lpBuffer, 
               ULONG nNumberOfBytesToRead, 
               PULONG lpNumberOfBytesRead, 
               PLARGE_INTEGER ByteOffset, 
               ULONG dwMilliseconds)
{
    OVERLAPPED ov;
    ov.Offset = ByteOffset->LowPart;
    ov.OffsetHigh = ByteOffset->HighPart;

    ULONG dwError = NOERROR;

    if (ov.hEvent = CreateEvent(0, 0, 0, 0))
    {
        dwError = ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, &ov) ? NOERROR : GetLastError();

        if (dwError == ERROR_IO_PENDING)
        {
            dwError = GetOverlappedResultEx(0/*yes, not need hFile*/, 
                &ov, lpNumberOfBytesRead, dwMilliseconds, FALSE) ? NOERROR : GetLastError();
        }

        CloseHandle(ov.hEvent);
    }
    else
    {
        dwError = GetLastError();
    }

    return dwError;
}
请注意,您必须(在异步文件的情况下)始终显式使用字节偏移量,从何处读取数据

GetOverlappedResultEx
通常执行下一步-检查
OVERLAPPED
-I/O是否完成(只需将
Internal
STATUS\u PENDING
进行比较),如果没有完成(仍然
Internal==STATUS\u PENDING
)您需要wait-it调用
WaitForSingleObjectEx
,其中
hEvent
来自
重叠
并传输
dwms
,以及
bAlertable
。如果
hEvent
发出信号(
wait\u OBJECT\u 0
WaitForSingleObjectEx
返回)
GetOverlappedResultEx
决定I/O完成并从
OVERLAPPED
返回结果,否则返回相应的错误


但是,如果
ReadFile
本身没有返回控制和等待-这里不可能执行某些操作。这意味着驱动程序开始在您的线程上等待。并且没有任何方法可以中断此驱动程序等待。看起来这正是您的情况。当您使用异步文件时-驱动程序不能等待,但是..有时(写得不好的驱动程序)这可以是WaitForSingleObject(overlapped.hEvent,5000)
?如果您愿意将自己限制在C++20,您可以使用一个协同程序来实现这一点。解释如何使用预构建的
wait_for
函数。该实现基本上按照Jonathan Potter的建议执行。@JonathanPotter-但是
GetOverlappedResultEx
和内部
WaitForSingleObjectEx(overlapped.hEvent,dw毫秒,bAlertable)
除了调用
GetOverlappedResultEx
不会在5秒钟(5000毫秒)内返回之外。相反,存储子系统继续需要10-20分钟才能返回故障。-这不可能。您在这里犯了一些错误
GetOverlappedResultEx(FSourceDiskHandle,重叠,out bytesRead,5000,False)
将不迟于5秒后返回,如果您改为说它将持续10-20分钟-这是您调用中的100%错误
GetOverlappedResultEx
-此api将仅等待来自重叠的事件,并且独立于文件。因此不能超过您的等待超时
`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].
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);
}
ULONG ReadFileTimeout(HANDLE hFile, 
               PVOID lpBuffer, 
               ULONG nNumberOfBytesToRead, 
               PULONG lpNumberOfBytesRead, 
               PLARGE_INTEGER ByteOffset, 
               ULONG dwMilliseconds)
{
    OVERLAPPED ov;
    ov.Offset = ByteOffset->LowPart;
    ov.OffsetHigh = ByteOffset->HighPart;

    ULONG dwError = NOERROR;

    if (ov.hEvent = CreateEvent(0, 0, 0, 0))
    {
        dwError = ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, &ov) ? NOERROR : GetLastError();

        if (dwError == ERROR_IO_PENDING)
        {
            dwError = GetOverlappedResultEx(0/*yes, not need hFile*/, 
                &ov, lpNumberOfBytesRead, dwMilliseconds, FALSE) ? NOERROR : GetLastError();
        }

        CloseHandle(ov.hEvent);
    }
    else
    {
        dwError = GetLastError();
    }

    return dwError;
}