ReadFile在Win7和Win2k8上不能异步工作

时间:2011-02-02 19:22:58

标签: winapi windows-7 asynchronous

根据MSDN,ReadFile可以通过两种不同的方式读取数据:同步和异步。 我需要第二个。下面的代码演示了OVERLAPPED struct:

的用法
#include <windows.h>
#include <stdio.h>
#include <time.h>

void Read()
{
    HANDLE hFile = CreateFileA("c:\\1.avi", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    if ( hFile == INVALID_HANDLE_VALUE )
    {
        printf("Failed to open the file\n");
        return;
    }

    int dataSize = 256 * 1024 * 1024;
    char* data = (char*)malloc(dataSize);
    memset(data, 0xFF, dataSize);

    OVERLAPPED overlapped;
    memset(&overlapped, 0, sizeof(overlapped));

    printf("reading: %d\n", time(NULL));
    BOOL result = ReadFile(hFile, data, dataSize, NULL, &overlapped);
    printf("sent: %d\n", time(NULL));


    DWORD bytesRead;
    result = GetOverlappedResult(hFile, &overlapped, &bytesRead, TRUE); // wait until completion - returns immediately
    printf("done: %d\n", time(NULL));

    CloseHandle(hFile);
}



int main()
{
        Read();
}

在Windows XP上输出是: 阅读:1296651896 发送:1296651896 完成:1296651899

这意味着ReadFile没有阻止并在同一时间返回imediatly,而阅读过程持续3秒。这是正常的异步读取。

但在Windows 7和Windows 2008上,我得到以下结果: 阅读:1296661205 发送:1296661209 完成:1296661209。 这是同步阅读的行为。

MSDN表示,异步ReadFile有时可以表现为同步(例如,当文件被压缩或加密时)。但是这种情况下的返回值应为TRUE且GetLastError() == NO_ERROR。 在Windows 7上,我得到FALSE和GetLastError() == ERROR_IO_PENDING。所以WinApi告诉我这是一个异步调用,但是当我看测试时,我发现它不是!

我不是唯一发现此“bug”的人:阅读ReadFile MSDN页面上的评论。

那么解决方案是什么?有人知道吗?在丹尼斯发现这种奇怪行为之后的14个月。

5 个答案:

答案 0 :(得分:4)

我不知道“c:\ 1.avi”文件的大小,但是你给Windows的缓冲区大小(256M!)可能足以容纳文件。因此,Windows决定读取整个文件并将其放入缓冲区中。你没有对windows说“我想要异步”,你说“我知道如何处理异步”。

只需将缓冲区大小改为1024,程序就会完全相同,但只读取1024个字节(并返回ERROR_IO_PENDING)。

通常,您执行异步操作是因为您希望在操作期间执行其他操作。请查看此处的示例:Testing for the End of a File,因为它演示了异步ReadFile。如果更改样本的缓冲区并将其设置为较大的值,则其行为应与您的完全相同。

PS:我建议你不要依赖时间样本来检查事物,使用返回代码和事件

答案 1 :(得分:1)

这可能与缓存有关。尝试打开非缓存文件(FILE_FLAG_NO_BUFFERING

修改

这实际上记录在ReadFile的MSDN文档中:

  

注意如果打开了文件或设备   用于异步I / O,后续调用   使用ReadFile等函数   处理一般返回   马上,但也可以表现   同步被阻止   执行。有关更多信息,请参阅   http://support.microsoft.com/kb/156932

答案 2 :(得分:1)

根据this,我怀疑它应该在你的情况下返回TRUE。但也可能是Win7 / Win2k8上的完成模式默认设置不同。 尝试使用SetFileCompletionNotificationModes()设置不同的模式。

答案 3 :(得分:1)

您是否尝试使用@Simon Mourier建议的活动?我知道文档说不需要这个事件,但是如果你看到@Simon Mourier提供的链接中的例子,它就是使用一个事件进行异步读取。

答案 4 :(得分:1)

Windows7 / Server2008具有不同的行为来解决GetOverlappedResultEx中可能发生的竞争条件。当您为这些操作系统编译时,Windows会检测到此并使用不同的行为。我发现这个邪恶的混乱。

这是一个链接: http://msdn.microsoft.com/en-us/library/dd371711(VS.85).aspx

我确定你过去曾多次读过这篇文章,但是自Win7以来,一些文本发生了变化 - 尤其是OVERLAPPED结构中的hEvent字段, http://msdn.microsoft.com/en-us/library/ms684342(v=VS.85).aspx

  

等功能   GetOverlappedResult和   同步等待功能重置   自动重置事件到无信号   州。 因此,您应该使用   手动重置事件; 如果你使用的话   自动重置事件,您的应用程序可以   如果你等待,就停止响应   操作完成然后调用   使用bWait获取GetOverlappedResult   参数设置为TRUE。

你可以做一个实验吗 - 请在你的OVERLAPPED结构中分配手动重置事件而不是自动重置事件? (我没有看到你的snippit中的分配 - 不要忘记创建事件并在将结构置零后设置'hEvent')