Я создал схему WriteFile с перекрытием, в которой один поток заполняет кольцевой буфер, перемещает указатель начала и вызывает WriteFile, а другой поток наблюдает за событиями записи OVERLAPPED для перемещения указателя хвоста. Логика ведёт себя как положено, но размер файла не увеличивается, он остаётся прежним и просто перезаписывает с позиции 0. Я проверял это, записывая инкрементное значение в память, записываемую в файл, и данные индекса увеличиваются, но он продолжает писать то, что эффективно fseek(0).
Вот код.
Сначала я открываю файл как GENERIC_WRITE и FILE_FLAG_OVERLAPPED и создаю 8 событий, по одному для каждой страницы, которую я собираюсь написать. Каждая страница имеет размер 256 КБ. Изначально я использовал только одно событие, но хотел убедиться, что проблема не в количестве событий.
void
FileWriter::open(string fn)
{
cout << "Opening file " << fn << endl;
m_file_handle = CreateFileA(
fn.c_str(),
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_FLAG_OVERLAPPED,
NULL);
if (m_file_handle == INVALID_HANDLE_VALUE)
{
throw runtime_error("Unable to create FileWriter file handle");
}
for (size_t i = 0; i < MAX_OVERLAPPED_WRITES; ++i)
{
m_events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
}
}
Теперь в другом потоке я вызываю эту функцию queue_page
всякий раз, когда заполняется буфер (8 x 256K страниц). m_ov
— это массив из 8 ПЕРЕКРЫТЫХ структур. До этого вызова m_pages[][] действует как кольцевой буфер, а m_pages[m_head][] расширяется, отправляя m_pages[old page][] для записи на диск.
void
FileWriter::queue_page(unsigned page, unsigned len)
{
ZeroMemory(&m_ov[page], sizeof(OVERLAPPED));
m_ov[page].hEvent = m_events[page];
if (!WriteFile(
m_file_handle,
(LPCVOID)m_pages[page],
len * sizeof(float),
NULL,
(LPOVERLAPPED)&m_ov[page]))
{
DWORD err = GetLastError();
if (err != ERROR_IO_PENDING)
{
cout << "GetLastError() = " << err << endl;
throw runtime_error("Failed to write overlapped");
}
}
}
Поток, который следует за асинхронной записью и перемещением указателя хвоста циклического буфера, прост:
void
FileWriter::wait(DWORD msec)
{
DWORD ret = WaitForMultipleObjects(MAX_OVERLAPPED_WRITES, m_events, FALSE, msec);
if (ret < MAXIMUM_WAIT_OBJECTS)
{
unsigned page = ret - WAIT_OBJECT_0;
if (page < MAX_OVERLAPPED_WRITES) {
ResetEvent(m_events[page]);
}
cout << "Page write completed " << ret << " page=" << page << endl;
m_tail = (m_tail + 1) & 0x7;
}
}
При проверке кольцевой буфер работает нормально и не переполняется, но выходной файл всегда имеет размер 256 КБ.
Не уверен, как отлаживать то, что происходит, или то, что я пропустил.
OVERLAPPED
. и легко привязать это к классу. или используйтеNtWriteFile
- это более просто и удобно для использования/передачи контекста. или возможная привязка файла к iocp, прямо или косвенно (скажем, через BindIoCompletionCallback). apc или iopc намного лучше, чем худшие события - person RbMm   schedule 15.02.2021page = ret - WAIT_OBJECT_0
здесьpage
ваш контекст и как вы его получили. действительно очень не удобно пользоваться событиями сравнивая apc и iocp. вы можете сказать, что вставили указательOVERLAPPED
на свой класс, страницу, дополнительную информацию об операции - и вернулись, когда ввод-вывод завершился. и не нужно использоватьFileWriter::wait
- person RbMm   schedule 15.02.2021