0

我正在尝试创建一个函数 ,ReadFileMany它模仿ReadFile的接口,它给出了一个要读取的 (offset, length) 对列表,并异步读取文件的所有部分。

我在内部ReadFileMany通过使用RegisterWaitForSingleObject指示线程池线程等待 I/O 完成来执行此操作,以便它可以ReadFile再次调用以读取文件的下一部分等。

我遇到的麻烦是我似乎无法模仿ReadFile.
具体来说,文件句柄本身可以像事件一样使用,而不需要事件句柄:

OVERLAPPED ov = {0};
DWORD nw;
if (ReadFile(hFile, buf, buf_size, &nw, &ov))
{
    if (WaitForSingleObject(hFile, INFINITE) == WAIT_OBJECT_0)
    {
        ...
    }
}

但是当然,如​​果用户等待文件句柄,他可能会收到中间读取完成的通知,而不是最终读取。

所以,在里面ReadFileMany,我别无选择,只能将hEvent参数传递ReadFile除了最后一部分之外的所有部分

问题是,有没有办法让用户在所有部分都被读取后等待文件句柄发出信号?

起初,答案似乎很明显:在阅读最后一部分时避免传递事件句柄!
如果读取成功,则可以正常工作,但如果有错误,则不行。如果ReadFile在上次读取时突然开始返回错误,我将需要手动将文件句柄设置为信号状态,以便让读者从对WaitForSingleObject(hFile).

但是似乎没有办法在不执行实际 I/O 的情况下将文件句柄ReadFile设置为信号状态......所以这就是我卡住的地方:有没有办法让我编写这个函数,使其表现得像在外面,还是不可能正确地做到这一点?

4

1 回答 1

0

假设您正在尝试读取单个文件的多个部分,我将简单地分配一个结构数组OVERLAPPED,为文件的每个请求部分分配一个结构,一次启动所有ReadFile()调用,然后用于WaitForMultipleObjects()等待所有要完成的 I/O,例如:

struct sReadInfo
{
    OVERLAPPED ov;
    LPVOID pBuffer;
    DWORD dwNumBytes;
    DWORD dwNumBytesRead;
    bool bPending;

    sReadInfo()
    {
        memset(&ov, 0, sizeof(OVERLAPPED));
        pBuffer = NULL;
        dwNumBytes = 0;
        dwNumBytesRead = 0;
        bPending = false;

        ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (!ov.hEvent) throw std::exception();
    }

    ~sReadInfo()
    {
        CloseHandle(hEvent);
    }
};

.

bool error = false;

try
{
    std::vector<sReadInfo> ri(numSections);

    std::vector<HANDLE> h;
    h.reserve(numSections);

    for(int i = 0; i < numSections; ++i)
    {
        ULARGE_INTEGER ul;
        ul.QuadPart = ...; // desired file offset to read from

        sReadInfo *r = &ri[i];

        r->ov.Offset = ul.LowPart;
        r->ov.OffsetHigh = ul.HighPart;

        r->pBuffer = ...; // desired buffer to read into
        r->dwNumBytes = ...; // desired number of bytes to read

        if (!ReadFile(hFile, r->pBuffer, r->dwNumBytes, &r->dwNumBytesRead, &r->ov))
        {
            if (GetLastError() != ERROR_IO_PENDING)
                throw std::exception();

            r->bPending = true;
            h.push_back(r->ov.hEvent);
        }
    }

    if (!h.empty())
    {
        if (WaitForMultipleObjects(h.size(), &h[0], TRUE, INFINITE) != WAIT_OBJECT_0)
            throw std::exception();
    }

    for (int i = 0; i < numSections; ++i)
    {
        sReadInfo *r = &ri[i];

        if (r->bPending)
            GetOverlappedResult(hFile, &r->ov, &r->dwNumBytesRead, FALSE);

        // ...
    }
}
catch (const std::exception &)
{
    CancelIo(hFile);
    return false;
}

return true;
于 2013-03-13T19:47:48.920 回答