Асинхронный файловый ввод/вывод в операционной системе WINDOWS, страница 13

Конечно же, поток может ожидать появления уведомления в очереди порта завершения. Однако при этом он не может следить за состоянием какого-либо объекта синхронизации или реагировать на переход в сигнальное состояние дескриптора консоли. Это является основной проблемой программы листинга 3. Эта программа вынуждена постоянно опрашивать порт завершения и консоль, не имея возможности перейти в режим ожидания изменения состояния и того и другого.

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

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

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

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

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

Листинг 3. Использование порта завершения

#include <windows.h>

#include <iostream.h>

#include <string.h>

#include <stdio.h> // sprintf

BOOL server=FALSE;

HANDLE OpenTalkPipe(char *host)

{

HANDLE rv;

if (!server)

{

char fn[1024];

sprintf(fn,"\\\\%s\\pipe\\talkpipe_awc",host);

cerr<<"Opening "<<fn<<"\n";

rv=CreateFile(fn,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,

NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);

}

else

{// работаем как сервер

rv=CreateNamedPipe("\\\\.\\pipe\\talkpipe_awc",PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,

PIPE_TYPE_BYTE|PIPE_READMODE_BYTE,1,1024,1024,1000,NULL);

}

return rv;

}

void main(int argc, char *argv[])

{

if (argc==1) server=TRUE;

HANDLE pipe=OpenTalkPipe(server?".":argv[1]);

HANDLE console=GetStdHandle(STD_INPUT_HANDLE);

OVERLAPPED over;

OVERLAPPED dummy;

HANDLE port= CreateIoCompletionPort(pipe, NULL, 1, 0);

char pipebuf;

DWORD inlen;

DWORD err;

if (pipe==INVALID_HANDLE_VALUE||port==INVALID_HANDLE_VALUE)

{

err=GetLastError();

cout<<"Ошибка открытия консоли или порта ("<<err<<")\n";

exit(9);

}

over.Internal=over.InternalHigh=over.Offset=over.OffsetHigh=0;

over.hEvent=NULL;

if (!SetConsoleMode(console,0))

{

cerr<<"Error: не могу войти в консоль\n"<<GetLastError();

exit(4);

}

cout<<"Ожидание соединения\n";

cout.flush();

//чтение консоли

do {

while (ReadFile(pipe,&pipebuf,1,&inlen,&over)) cout<<pipebuf;

err=GetLastError();

} while (err==ERROR_PIPE_LISTENING);  // ожидание клиента