
Задание 23. Асинхронное чтение из файла.
Тип задания: разработка.
Уровень сложности: повышенный.
При асинхронном чтении данных из файла, после выдачи команды на чтение, поток не блокируется до завершения операции, а продолжает свою работу. В ОС Windows асинхронный ввод-вывод называется перекрывающимся (overlapped) вводом-выводом. Асинхронный ввод-вывод в ОС Windows выполняют «обычные» ф-ции ReadFile и WriteFile, но файл при этом должен быть открыт в режиме FILE_FLAG_OVERLAPPED.
О завершении операции ввода-вывода можно узнать либо по факту перехода дескриптора файла в сигнальное состояние (этот способ не годится, если с файлом одновременно работает несколько потоков ввода-вывода), либо используя специальное событие, устанавливаемое ОС по завершению операции в сигнальное состояние. Дескриптор этого события должен находиться в структуре типа OVERLAPPED, адрес которой передается ф-циям асинхронного ввода-вывода.
При одновременной работе нескольких асинхронных операций ввода-вывода для каждой из них нужно определить свою структуру, в которой указать дескриптор отдельного события, связанного с данной операцией ввода-вывода.
Рекомендуемые источники:
-
Столлингс В. Операционные системы, 4-е изд.: - М.: Изд. дом "Вильямс", 2002.
-
Электронный справочник «Win32 Programmer’s Reference» из состава «MS SDK Help Files».
Проектное задание:
-
Ознакомиться с механизмом асинхронного ввода-вывода ОС Windows.
-
Изучить назначение и использование функций API для работы с событиями (events).
-
Выполнить обзор соответствующих функций API.
-
Разработать и реализовать программу согласно условию задачи.
-
Разработать набор тестов для демонстрации работоспособности и правильности программы.
-
Подготовить отчет по курсовому проекту.
Задача:
Реализовать программы для асинхронного чтения данных из файла с использованием дескриптора и множественного асинхронного чтения с использованием событий.
Спроектировать, закодировать и отладить программы.
Язык реализации – произвольный, ОС – Windows.
ПРИЛОЖЕНИЕ Б
(Блок- схема алгоритма работы программы)
ПРИЛОЖЕНИЕ В
(Листинг программной реализации асинхронного чтения с файла)
#include <iostream>
#include <windows.h>
#include <process.h>
using namespace std;
const int N=1019000;//Количество читаемых байт
HANDLE hFile;
HANDLE hEvent1,hEvent2;
int threadsWork=0;
char* rus(char* message)
{
char* tmp=new char[strlen(message)+1];
CharToOem(message,tmp);
return tmp;
}
void Monitor1(void *)
{
DWORD dwWaitResult=1;
while(dwWaitResult!=WAIT_OBJECT_0)//Если чтение не завершилось
{
dwWaitResult = WaitForSingleObject(hEvent1,1); //Ожидаем событие окончания чтения
cout<<rus("\nВремя ожидания 1 потока вышло");
}
threadsWork--;
cout<<rus("\n1 поток завершил чтение");
_endthread();//завершение потока
}
void Monitor2(void *)
{
DWORD dwWaitResult=1;
while(dwWaitResult!=WAIT_OBJECT_0)//Если чтение не завершилось
{
dwWaitResult = WaitForSingleObject(hEvent2,1);//Ожидаем событие окончания чтения
cout<<rus("\nВремя ожидания 2 потока вышло");
}
threadsWork--;
cout<<rus("\n2 поток завершил чтение");
_endthread();//завершение потока
}
void thread1(void*)
{
char inBuffer[N+1]="\0";
DWORD nBytesToRead=N,nBytesRead,dwError;
hEvent1=CreateEvent(NULL,true,false,"event1");//создаем событие окончания чтения
if(hEvent1==NULL)
{
char error[30];
sprintf(error,"Такое событие уже существует (ошибка %d)",GetLastError());
cout<<rus(error)<<endl;
system("pause");
exit(-1);
}
OVERLAPPED gOverLapped;
// подготавливаем поля структуры асинхронной операции
gOverLapped.Offset = 0;
gOverLapped.OffsetHigh = 0;
gOverLapped.hEvent = hEvent1;
bool bResult;
//do{
// пытаемся провести асинхронную операцию чтения
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead,&gOverLapped) ;
// если возникает проблема или асинхронная операция
// все еще ожидает обработки ...
if (!bResult&&GetLastError()==ERROR_HANDLE_EOF)
{
// мы достигли конца файла
// в течение вызова к ReadFile
cout<<rus("\nМы достигли конца файла");
//break;
} // конец процедуры if
//cout<<" 1="<<inBuffer<<endl;
//gOverLapped.Offset+=nBytesRead;}while(bResult);
cout<<rus("\n1 поток закончил свою работу!");
CloseHandle(hEvent1);//Закрытие дескриптора события
_endthread();//завершение потока
}
void thread2(void*)
{
char inBuffer[N+1]="\0";
DWORD nBytesToRead=N,nBytesRead,dwError;
hEvent2=CreateEvent(NULL,true,false,"event2");//создаем событие окончания чтения
if(hEvent2==NULL)
{
char error[30];
sprintf(error,"Такое событие уже существует (ошибка %d)",GetLastError());
cout<<rus(error)<<endl;
system("pause");
exit(-1);
}
OVERLAPPED gOverLapped;
// подготавливаем поля структуры асинхронной операции
gOverLapped.Offset = 0;
gOverLapped.OffsetHigh = 0;
gOverLapped.hEvent = hEvent2;
bool bResult;
//do{
// пытаемся провести асинхронную операцию чтения
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead,&gOverLapped) ;
// если достигли конца файла
if (!bResult&&GetLastError()==ERROR_HANDLE_EOF)
{
// мы достигли конца файла
// в течение вызова к ReadFile
cout<<rus("\nМы достигли конца файла");
//break;
} // конец процедуры if
//cout<<" 2="<<inBuffer<<endl;
//gOverLapped.Offset+=nBytesRead;}while(bResult);
cout<<rus("\n2 поток закончил свою работу!");
CloseHandle(hEvent2);//Закрытие дескриптора события
_endthread();//завершение потока
}
void main()
{
if (_beginthread(Monitor1,1024,NULL)==-1)//Запуск монитора события первого потока
cout <<rus("\nОшибка запуска потока 1 монитора\n")<< endl;
if (_beginthread(Monitor2,1024,NULL)==-1)//Запуск монитора события второго потока
cout << rus("\nОшибка запуска потока 2 монитора\n")<< endl;
char fileName[]="D:\\file1.txt\0";
hFile=CreateFile(fileName, // Открываемый файл
GENERIC_READ, // Открываем для чтения
FILE_SHARE_READ, // Для совместного чтения
NULL, // Защита по умолчанию
OPEN_EXISTING, // Только существующий файл
FILE_ATTRIBUTE_NORMAL, // Обычный файл
NULL); // Атрибутов шаблона нет
if (hFile == INVALID_HANDLE_VALUE) //Если возникла ошибка
{
char error[30];
sprintf(error,"Не удалось открыть файл (ошибка %d)",GetLastError());
cout<<rus(error)<<endl;
system("pause");
exit(-1);
}
if (_beginthread(thread2,1024,NULL)==-1)//Запуск второго потока чтения
cout <<rus("\nОшибка запуска 2 потока\n")<< endl;
else threadsWork++;
if (_beginthread(thread1,1024,NULL)==-1)//Запуск первого потока чтения
cout <<rus("\nОшибка запуска 1 потока\n")<< endl;
else threadsWork++;
while(threadsWork);//Ожидание завершения потоков чтения
CloseHandle(hFile);//Закрытие файла
cout<<endl;
system("pause");
}