Выбрать главу

 nRecord = FileSize.QuadPart / REC_SIZE;

 if ((FileSize.QuadPart % REC_SIZE) != 0) nRecord++;

 CurPosIn.QuadPart = 0;

 for (ic = 0; ic < MAX_OVRLP; ic++) {

  OverLapIn[ic].hEvent = (HANDLE)ic; /* Перегрузить событие. */

  OverLapOut[ic].hEvent = (HANDLE)ic; /* Поля. */

  OverLapIn[ic].Offset = CurPosIn.LowPart;

  OverLapIn[ic].OffsetHigh = CurPosIn.HighPart;

  if (CurPosIn.QuadPart < FileSize.QuadPart) ReadFileEx(hInputFile, AsRec[ic], REC_SIZE, &OverLapIn [ic], ReadDone);

  CurPosIn.QuadPart += (LONGLONG)REC_SIZE;

 }

 /* Выполняются все операции чтения. Войти в состояние дежурного ожидания и оставаться в нем до тех пор, пока не будут обработаны все записи.*/

 nDone = 0; 

 while (nDone < 2 * nRecord) SleepEx(INFINITE, TRUE);

 CloseHandle(hInputFile);

 CloseHandle(hOutputFile);

 _tprintf(_T("Преобразование из ASCII в Unicode завершено.\n"));

 return 0;

}

static VOID WINAPI ReadDone(DWORD Code, DWORD nBytes, LPOVERLAPPED pOv) {

 /* Чтение завершено. Преобразовать данные и инициировать запись. */

 LARGE_INTEGER CurPosIn, CurPosOut;

 DWORD ic, i;

 nDone++;

 /* Обработать запись и инициировать операцию записи. */

 ic = (DWORD)(pOv->hEvent);

 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 < nBytes; i++) UnRec[ic][i] = AsRec[ic][i];

 WriteFileEx(hOutputFile, UnRec[ic], nBytes*2, &OverLapOut[ic], WriteDone);

 /* Подготовить структуру OVERLAPPED для следующего чтения. */

 CurPosIn.QuadPart += REC_SIZE * (LONGLONG)(MAX_OVRLP);

 OverLapIn[ic].Offset = CurPosIn.LowPart;

 OverLapIn[ic].OffsetHigh = CurPosIn.HighPart;

 return;

}

static VOID WINAPI WriteDone(DWORD Code, DWORD nBytes, LPOVERLAPPED pOv) {

 /* Запись завершена. Инициировать следующую операцию чтения. */

 LARGE_INTECER CurPosIn;

 DWORD ic;

 nDone++;

 ic = (DWORD)(pOv->hEvent);

 CurPosIn.LowPart = OverLapIn[ic].Offset;

 CurPosIn.HighPart = OverLapIn[ic].OffsetHigh;

 if (CurPosIn.QuadPart < FileSize.QuadPart) {

  ReadFileEx(hInputFile, AsRec[ic], REC_SIZE, &OverLapIn[ic], ReadDone);

 }

 return;

}

Асинхронный ввод/вывод сиспользованием нескольких потоков

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

Однако Windows обеспечивает многопоточную поддержку, поэтому становится возможным достижение того же эффекта за счет выполнения синхронных операций ввода/вывода в нескольких, выполняемых независимо потоках. Ранее эти возможности уже были продемонстрированы на примере многопоточных серверов и программы grepMT (глава 7). Кроме того, потоки обеспечивают концептуально последовательный и, предположительно, гораздо более простой способ выполнения асинхронных операций ввода/вывода. В качестве альтернативы методам, используемым в программах 14.1 и 14.2, можно было бы предоставить каждому потоку собственный дескриптор файла, и тогда каждый из потоков мог бы обрабатывать в синхронном режиме каждую четвертую запись.

Такой способ использования потоков продемонстрирован в программе atouMT, которая в книге не приводится, но включена в материал, размещенный на Web-сайте. Программа atouMT не только способна выполняться под управлением любой версии Windows, но и более проста по сравнению с любым из двух вариантов программ асинхронного ввода/вывода, поскольку учет использования ресурсов в этом случае менее сложен. Каждый поток просто поддерживает собственные буферы в собственном стеке и выполняет в цикле последовательность синхронных операций чтения, преобразования и записи. При этом производительность программы остается на достаточно высоком уровне.

Примечание

В программе atouMT.с, которая находится на Web-сайте, содержатся комментарии по поводу нескольких возможных "ловушек", которые могут поджидать вас при организации доступа одновременно нескольких потоков к одному и тому же файлу. В частности, все отдельные дескрипторы файлов должны создаваться с помощью функции CreateHandle, а не функции DuplicateHandle.

Лично я предпочитаю использовать многопоточную обработку файлов, а не асинхронные операции ввода/вывода. Потоки легче программировать, и в большинстве случаев они обеспечивают более высокую производительность.

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