Книга: Системное программирование в среде Windows

Пример: преобразование файлов с использованием перекрывающегося ввода/вывода и множественной буферизации

Пример: преобразование файлов с использованием перекрывающегося ввода/вывода и множественной буферизации

Программа 2.4 (atou) осуществляла преобразование ASCII-файла к кодировке UNICODE путем последовательной обработки файла, а в главе 5 было показано, как выполнить такую же последовательную обработку с помощью отображения файлов. В программе 14.1 (atouOV) та же самая задача решается с использованием перекрывающегося ввода/вывода и множественных буферов, в которых хранятся записи фиксированного размера.

Рисунок 14.1 иллюстрирует организацию программы с четырьмя буферами фиксированного размера. Программа реализована таким образом, чтобы количество буферов можно было определять при помощи символической константы препроцессора, но в нижеследующем обсуждении мы будем предполагать, что существуют четыре буфера.

Сначала в программе выполняется инициализация всех элементов структур OVERLAPPED, определяющих события и позиции в файлах. Для каждого входного и выходного буферов предусмотрена отдельная структура OVERLAPPED. После этого для каждого из входных буферов инициируется операция перекрывающегося чтения. Далее с помощью функции WaitForMultipleObjects в программе организуется ожидание одиночного события, указывающего на завершение чтения или записи. При завершении операции чтения входной буфер копируется и преобразуется в соответствующий выходной буфер, после чего инициируется операция записи. При завершении записи инициируется следующая операция чтения. Заметьте, что события, связанные с входными и выходными буферами размещаются в единственном массиве, который используется в качестве аргумента при вызове функции WaitForMultipleObjects. 


Рис. 14.1. Модель асинхронного обновления файла

Программа 14.1. atouOV: преобразование файла с использованием перекрывающегося ввода/вывода 

/* Глава 14. atouOV
   Преобразование файла из кодировки ASCII в кодировку Unicode с использованием перекрывающегося ввода/вывода. Программа работает только в Windows NT. */
#include "EvryThng.h"
#define MAX_OVRLP 4 /* Количество перекрывающихся операций ввода/вывода.*/ 
#define REC_SIZE 0x8000 /* 32 Кбайт: Минимальный размер записи, обеспечивающий приемлемую производительность. */
#define UREC_SIZE 2 * REC_SIZE
int _tmain(int argc, LPTSTR argv[]) {
 HANDLE hInputFile, hOutputFile;
 /* Каждый из элементов определенных ниже массивов переменных */
 /* и структур соответствует отдельной незавершенной операции */
 /* перекрывающегося ввода/вывода. */
 DWORD nin[MAX_OVRLP], nout[MAX_OVRLP], ic, i;
 OVERLAPPED OverLapIn[MAX_OVRLP], OverLapOut[MAX_OVRLP];
 /* Необходимость использования сплошного, двумерного массива */
 /* диктуется Функцией WaitForMultipleObjects. */
 /* Значение 0 первого индекса соответствует чтению, значение 1 – записи.*/
 HANDLE hEvents[2][MAX_OVRLP];
 /* В каждом из определенных ниже двух буферных массивов первый индекс */
 /* нумерует операции ввода/вывода. */
 CHAR AsRec[MAX_OVRLP][REC_SIZE];
 WCHAR UnRec[MAX_OVRLP][REC_SIZE];
 LARGE_INTEGER CurPosIn, CurPosOut, FileSize;
 LONGLONG nRecord, iWaits;
 hInputFile = CreateFile(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
 hOutputFile = CreateFile(argv[2], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
 /* Общее количество записей, подлежащих обработке, вычисляемое */
 /* на основе размера входного файла. Запись, находящаяся в конце, */
 /* может быть неполной. */
 FileSize.LowPart = GetFileSize(hInputFile, &FileSize.HighPart);
 nRecord = FileSize.QuadPart / REC_SIZE;
 if ((FileSize.QuadPart % REC_SIZE) != 0) nRecord++;
 CurPosIn.QuadPart = 0;
 for (ic = 0; ic < MAX_OVRLP; ic++) {
  /* Создать события чтения и записи для каждой структуры OVERLAPPED.*/
  hEvents[0][ic] = OverLapIn[ic].hEvent /* Событие чтения.*/
   = CreateEvent(NULL, TRUE, FALSE, NULL);
  hEvents[1][ic] = OverLapOut[ic].hEvent /* Событие записи. */
   = CreateEvent(NULL, TRUE, FALSE, NULL);
  /* Начальные позиции в файле для каждой структуры OVERLAPPED. */
  OverLapIn[ic].Offset = CurPosIn.LowPart;
  OverLapIn[ic].OffsetHigh = CurPosIn.HighPart;
  /* Инициировать перекрывающуюся операцию чтения для данной структуры OVERLAPPED. */
  if (CurPosIn.QuadPart < FileSize.QuadPart) ReadFile(hInputFile, AsRec[ic], REC_SIZE, &nin[ic], &OverLapIn[ic]); 
  CurPosIn.QuadPart += (LONGLONG)REC_SIZE;
 }
 /* Выполняются все операции чтения. Ожидать завершения события и сразу же сбросить его. События чтения и записи хранятся в массиве событий рядом друг с другом. */
 iWaits =0; /* Количество выполненных к данному моменту операций ввода/вывода. */
 while (iWaits < 2 * nRecord) {
  ic = WaitForMultipleObjects(2 * MAX_OVRLP, hEvents[0], FALSE, INFINITE) – WAIT_OBJECT_0;
  iWaits++; /* Инкрементировать счетчик выполненных операций ввода вывода.*/
  ResetEvent(hEvents[ic / MAX_OVRLP][ic % MAX_OVRLP]);
  if (ic < MAX_OVRLP) {
   /* Чтение завершено. */
   GetOverlappedResult(hInputFile, &OverLapIn[ic], &nin[ic], FALSE);
   /* Обработать запись и инициировать операцию записи. */
   CurPosIn.LowPart = OverLapIn[ic].Offset;
   CurPosIn.HighPart = OverLapIn[ic].OffsetHigh;
   CurPosOut.QuadPart = (CurPosIn.QuadPart / REC_SIZE) * UREC_SIZE;
   OverLapOut[ic].Offset = CurPosOut.LowPart;
   OverLapOut[ic].OffsetHigh = CurPosOut.HighPart;
   /* Преобразовать запись из ASCII в Unicode. */
   for (i =0; i < REC_SIZE; i++) UnRec[ic][i] = AsRec[ic][i];
   WriteFile(hOutputFile, UnRec[ic], nin[ic] * 2, &nout[ic], &OverLapOut[ic]);
   /* Подготовиться к очередному чтению, которое будет инициировано после того, как завершится начатая выше операция записи. */
   CurPosIn.QuadPart += REC_SIZE * (LONGLONG)(MAX_OVRLP);
   OverLapIn[ic].Offset = CurPosIn.LowPart;
   OverLapIn[ic].OffsetHigh = CurPosIn.HighPart;
  } else if (ic < 2 * MAX_OVRLP) { /* Операция записи завершилась. */
   /* Начать чтение. */
   ic –= MAX_OVRLP; /* Установить индекс выходного буфера. */
   if (!GetOverlappedResult (hOutputFile, &OverLapOut[ic], &nout[ic], FALSE)) ReportError(_T("Ошибка чтения."), 0, TRUE);
   CurPosIn.LowPart = OverLapIn[ic].Offset;
   CurPosIn.HighPart = OverLapIn[ic].OffsetHigh;
   if (CurPosIn.QuadPart < FileSize.QuadPart) {
    /* Начать новую операцию чтения. */
    ReadFile(hInputFile, AsRec[ic], REC_SIZE, &nin[ic], &OverLapIn[ic]);
   }
  }
 } 
 /* Закрыть все события. */
 for (ic = 0; ic < MAX_OVRLP; ic++) {
  CloseHandle(hEvents[0][ic]);
  CloseHandle(hEvents[1][ic]);
 }
 CloseHandle(hInputFile);
 CloseHandle(hOutputFile);
 return 0;
}
 

Программа 14.1 способна работать только под управлением Windows NT. Средства асинхронного ввода/вывода Windows 9x не позволяют использовать дисковые файлы. В приложении В приведены результаты и комментарии, свидетельствующие о сравнительно низкой производительности программы atouOV. Как показали эксперименты, для достижения приемлемой производительности размер буфера должен составлять, по крайней мере, 32 Кбайт, но даже и в этом случае обычный синхронный ввод/вывод работает быстрее. К тому же, производительность этой программы не повышается и в условиях SMP, поскольку в данном примере, в котором обрабатываются всего лишь два файла, ЦП не является критическим ресурсом.

Оглавление книги


Генерация: 1.421. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз