【问题标题】:Read lines from file async using WINAPI ReadFile使用 WINAPI ReadFile 从文件异步读取行
【发布时间】:2015-09-04 00:46:12
【问题描述】:

我需要同时从文件中读取几行,即异步。 文件中的行大小相同。

例如,我需要读取文件的第二行和第四行来分隔变量或数组。

我更习惯于 c# 的 async/await 并且所有这些 OVERLAPPED 对我来说有点难以理解。

使用msdn示例,我实现了这一点,它只是从文件中读取数据作为一个字符串(这甚至是异步读取吗?):

BOOL ReadFromFileAsync(PCTSTR path)
{
    HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        _tprintf_s(TEXT("INVALID_HANDLE_VALUE\n"));
        return FALSE;
    }

    BOOL bResult;
    BYTE bReadBuf[2048];

    OVERLAPPED oRead = { 0 };
    oRead.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    bResult = ReadFile(hFile, bReadBuf, _countof(bReadBuf), NULL, &oRead);

    if (!bResult && GetLastError() != ERROR_IO_PENDING)
    {
        _tprintf_s(TEXT("ERROR io pending"));
        CloseHandle(hFile);
        return FALSE;
    }

    // perform some stuff asynchronously
    _tprintf_s(TEXT("HEY\n"));

    HANDLE hEvents[2];
    hEvents[0] = oRead.hEvent;
    hEvents[1] = oRead.hEvent;
    DWORD dwWaitRes = WaitForMultipleObjects(_countof(hEvents), hEvents, FALSE, INFINITE);

    switch (dwWaitRes - WAIT_OBJECT_0)
    {
        case 0: // reading finished
            _tprintf_s(TEXT("String that was read from file: "));

            for (int i = 0; i < oRead.InternalHigh; ++i)
                _tprintf_s(TEXT("%c"), bReadBuf[i]);

            _tprintf_s(TEXT("\n"));
            break;

        default:
            _tprintf_s(TEXT("Nooo"));
    }

    CloseHandle(hFile);

    return TRUE;
}

您能帮我从文件中异步读取两行吗?

我应该使用SetFilePointer 来移动吗?

【问题讨论】:

  • 只有在异步读取繁忙时您还有其他事情要做时,使用重叠 I/O 才有用。您没有任何有用的事情可做,在开始读取后立即调用 WaitForMultipleObjects()。所以不要使用它。
  • @HansPassant:上面的代码确实说:“异步执行一些东西”,所以丹尼斯只是没有显示在读取过程中正在做什么......

标签: c++ multithreading winapi createfile overlapped-io


【解决方案1】:

当您打开带有FILE_FLAG_OVERLAPPED 标志的文件,然后使用带有ReadFile()OVERLAPPED 结构时,请使用OVERLAPPED.OffsetOVERLAPPED.OffsetHigh 字段来指定应开始读取的字节偏移量。此外,如果同时运行它们,则必须为每个 ReadFile() 使用单独的 OVERLAPPED 实例。这在documentation中有明确说明:

如果使用FILE_FLAG_OVERLAPPED打开hFile,则lpOverlapped参数必须指向一个有效的且唯一的 OVERLAPPED结构,否则函数会错误地报告读取操作已完成。

对于支持字节偏移的hFile,如果你使用这个参数你必须指定开始从文件或设备读取的字节偏移。此偏移量通过设置 OVERLAPPED 结构的 Offset 和 OffsetHigh 成员来指定。对于不支持字节偏移的 hFile,Offset 和 OffsetHigh 将被忽略。

由于您的行长度相同,您可以轻松计算第二行和第四行的偏移量,然后针对这些偏移量发出两个异步ReadFile() 调用,然后根据需要等待这两个操作完成。

顺便说一句,你真的不应该使用FILE_FLAG_NO_BUFFERING,除非你真的知道你在做什么:

对于使用 FILE_FLAG_NO_BUFFERING 标志成功处理使用 CreateFile 打开的文件有严格要求,有关详细信息,请参阅 File Buffering

尝试类似的方法:

#include <vector>

BOOL ReadFromFileAsync(PCTSTR path)
{
    BOOL bResult = FALSE;

    HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED /*| FILE_FLAG_NO_BUFFERING*/, NULL);    
    if (hFile == INVALID_HANDLE_VALUE)
    {
        _tprintf_s(TEXT("Error opening file: %s\n"), path);
        return FALSE;
    }

    DWORD dwLineSize = ...; // size of each line, in bytes
    std::vector<BYTE> bSecondLineBuf(dwLineSize);
    std::vector<BYTE> bFourthLineBuf(dwLineSize);

    OVERLAPPED oReadSecondLine = { 0 };
    OVERLAPPED oReadFourthLine = { 0 };

    oReadSecondLine.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!oReadSecondLine.hEvent)
    {
        _tprintf_s(TEXT("Error creating I/O event for reading second line\n"));
        goto done;
    }
    oReadSecondLine.Offset = ...; // offset of second line

    oReadFourthLine.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!oReadFourthLine.hEvent)
    {
        _tprintf_s(TEXT("Error creating I/O event for reading fourth line\n"));
        goto done;
    }
    oReadFourthLine.Offset = ...; // offset of fourth line

    if (!ReadFile(hFile, &bSecondLineBuf[0], dwLineSize, NULL, &oReadSecondLine))
    {
        if (GetLastError() != ERROR_IO_PENDING)
        {
            _tprintf_s(TEXT("Error starting I/O to read second line\n"));
            goto done;
        }
    }

    if (!ReadFile(hFile, &bFourthLineBuf[0], dwLineSize, NULL, &oReadFourthLine))
    {
        if (GetLastError() != ERROR_IO_PENDING)
        {
            _tprintf_s(TEXT("Error starting I/O to read fourth line\n"));
            CancelIo(hFile);
            goto done;
        }
    }

    // perform some stuff asynchronously
    _tprintf_s(TEXT("HEY\n"));

    HANDLE hEvents[2];
    hEvents[0] = oReadSecondLine.hEvent;
    hEvents[1] = oReadFourthLine.hEvent;

    DWORD dwWaitRes = WaitForMultipleObjects(_countof(hEvents), hEvents, TRUE, INFINITE);
    if (dwWaitRes == WAIT_FAILED)
    {
        _tprintf_s(TEXT("Error waiting for I/O to finish\n"));
        CancelIo(hFile);
        goto done;
    }    

    _tprintf_s(TEXT("Strings that were read from file: "));

    for (int i = 0; i < oReadSecondLine.InternalHigh; ++i)
        _tprintf_s(TEXT("%c"), (TCHAR) &bSecondLineBuf[i]);    
    _tprintf_s(TEXT("\n"));

    for (int i = 0; i < oReadFourthLine.InternalHigh; ++i)
        _tprintf_s(TEXT("%c"), (TCHAR) &bFourthLineBuf[i]);    
    _tprintf_s(TEXT("\n"));

done:
    if (oReadSecondLine.hEvent) CloseHandle(oReadSecondLine.hEvent);
    if (oReadFourthLine.hEvent) CloseHandle(oReadFourthLine.hEvent);
    CloseHandle(hFile);

    return bResult;
}

或者:

#include <vector>

BOOL ReadFromFileAsync(PCTSTR path)
{
    BOOL bResult = FALSE;

    HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED /*| FILE_FLAG_NO_BUFFERING*/, NULL);    
    if (hFile == INVALID_HANDLE_VALUE)
    {
        _tprintf_s(TEXT("Error opening file: %s\n"), path);
        return FALSE;
    }

    DWORD dwLineSize = ...; // size of each line, in bytes
    std::vector<BYTE> bSecondLineBuf(dwLineSize);
    std::vector<BYTE> bFourthLineBuf(dwLineSize);

    OVERLAPPED oReadSecondLine = { 0 };
    OVERLAPPED oReadFourthLine = { 0 };

    oReadSecondLine.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!oReadSecondLine.hEvent)
    {
        _tprintf_s(TEXT("Error creating I/O event for reading second line\n"));
        goto done;
    }
    oReadSecondLine.Offset = ...; // offset of second line

    oReadFourthLine.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!oReadFourthLine.hEvent)
    {
        _tprintf_s(TEXT("Error creating I/O event for reading fourth line\n"));
        goto done;
    }
    oReadFourthLine.Offset = ...; // offset of fourth line

    if (!ReadFile(hFile, &bSecondLineBuf[0], dwLineSize, NULL, &oReadSecondLine))
    {
        if (GetLastError() != ERROR_IO_PENDING)
        {
            _tprintf_s(TEXT("Error starting I/O to read second line\n"));
            goto done;
        }
    }

    if (!ReadFile(hFile, &bFourthLineBuf[0], dwLineSize, NULL, &oReadFourthLine))
    {
        if (GetLastError() != ERROR_IO_PENDING)
        {
            _tprintf_s(TEXT("Error starting I/O to read fourth line\n"));
            CancelIo(hFile);
            goto done;
        }
    }

    // perform some stuff asynchronously
    _tprintf_s(TEXT("HEY\n"));

    HANDLE hEvents[2];
    hEvents[0] = oReadSecondLine.hEvent;
    hEvents[1] = oReadFourthLine.hEvent;

    OVERLAPPED* pOverlappeds[2];
    pOverlappeds[0] = &oReadSecondLine;
    pOverlappeds[1] = &oReadFourthLine;

    BYTE* pBufs[2];
    pBufs[0] = &bSecondLineBuf[0];
    pBufs[1] = &bFourthLineBuf[0];

    DWORD dwNumReads = _countof(hEvents);

    do
    {
        DWORD dwWaitRes = WaitForMultipleObjects(dwNumReads, hEvents, FALSE, INFINITE);
        if (dwWaitRes == WAIT_FAILED)
        {
            _tprintf_s(TEXT("Error waiting for I/O to finish\n"));
            CancelIo(hFile);
            goto done;
        }    

        if ((dwWaitRes >= WAIT_OBJECT_0) && (dwWaitRes < (WAIT_OBJECT_0+dwNumReads)))
        {
            DWORD dwIndex = dwWaitRes - WAIT_OBJECT_0;

            _tprintf_s(TEXT("String that was read from file: "));

            for (int i = 0; i < pOverlappeds[dwIndex]->InternalHigh; ++i)
                _tprintf_s(TEXT("%c"), (TCHAR) pBufs[dwIndex][i]);    
            _tprintf_s(TEXT("\n"));

           --dwNumReads;
           if (dwNumReads == 0)
               break;

           if (dwIndex == 0)
           {
               hEvents[0] = hEvents[1];
               pOverlappeds[0] = pOverlappeds[1];
               pBufs[0] = pBufs[1];
           }
        }
    }
    while (true);

done:
    if (oReadSecondLine.hEvent) CloseHandle(oReadSecondLine.hEvent);
    if (oReadFourthLine.hEvent) CloseHandle(oReadFourthLine.hEvent);
    CloseHandle(hFile);

    return bResult;
}

【讨论】:

    猜你喜欢
    • 2018-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-22
    • 2012-10-14
    • 1970-01-01
    • 2012-12-07
    相关资源
    最近更新 更多