Windows 重叠的 IO 实际上是阻塞的

Windows overlapped IO actually blocks

我正在尝试 Windows 重叠 IO,但我似乎无法让它异步工作。我已经编译并 运行 下面的程序,但它从不打印任何东西,它只是默默地完成。我看过小读可以同步,所以我特意选择读512MB。

  const DWORD Size = 1<<29; // 512MB
  char* Buffer = (char*)malloc(Size);
  DWORD BytesRead;
  OVERLAPPED Overlapped;
  memset(&Overlapped, 0, sizeof(Overlapped));

  HANDLE File = CreateFile("BigFile", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL);
  assert(File!=INVALID_HANDLE_VALUE);

  DWORD Result = ReadFileEx(File, Buffer, Size, &Overlapped, NULL); // This line takes 150ms according to the debugger
  assert(Result);

  while(!GetOverlappedResult(File, &Overlapped, &BytesRead, FALSE)) {
    printf("Waiting...\n");
  }

作为附加信息,我已将代码单步执行到调试器中,并且 Overlapped.InternalHigh 值在 ReadFileEx 调用期间得到更新(与 Size 具有相同的值)。

我尝试将 malloc 替换为 VirtualAlloc,将 ReadFileEx 替换为 ReadFile,添加 FILE_FLAG_NO_BUFFERING,并检查了 return ReadFile 的值为 0,读取后 GetLastError 的值为 ERROR_IO_PENDING。我试过使用 RAMMap 查看文件是否在缓存中,但只有 96KB 在那里。

我是 运行宁 Windows 10(版本 1703),内存为 8GB。

好的,我已经开始工作了,多亏了@RbMm。

除了 FILE_FLAG_NO_BUFFERING 标志和 ReadFile 的使用外,内存分配没有改变任何东西。我尝试在 ReadFileEx 之后使用 SleepEx,它在 0 处引发了访问冲突,这证明了@RbMm 的观点,即 lpCompletionRoutine 不是可选的,而是强制性的。 (对我来说这意味着我要使用 ReadFile 因为我不想要完成例程)

至于为什么我花了这么长时间才意识到发生了什么:我太相信调试器了,显然闯入调试器并没有停止 IO 进程,这意味着内存仍在 OVERLAPPED 结构,这让我觉得事情是瞬时的。最重要的是,我预计 ReadFile 会很快达到 return,但如果我尝试读取 512MB 实际上需要 20 毫秒,当我请求较小的数量时它会快得多。

感谢大家的建议:)

为了完整起见,这里是工作程序:

  const DWORD Size = 1<<20;
  char* Buffer = (char*)malloc(Size);
  DWORD BytesRead;
  OVERLAPPED Overlapped;
  memset(&Overlapped, 0, sizeof(Overlapped));

  HANDLE File = CreateFile("BigFile", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING|FILE_FLAG_OVERLAPPED, NULL);
  assert(File!=INVALID_HANDLE_VALUE);
  DWORD Result = ReadFile(File, Buffer, Size, NULL, &Overlapped);
  assert(Result==FALSE && GetLastError()==ERROR_IO_PENDING);
  while(!GetOverlappedResult(File, &Overlapped, &BytesRead, FALSE)) {
    printf("Waiting...\n");
  }