KnigaRead.com/

Олег Титов - Работа с COM и LPT в Win32.

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Олег Титов, "Работа с COM и LPT в Win32." бесплатно, без регистрации.
Назад 1 ... 5 6 7 8 9 Вперед
Перейти на страницу:

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

DWORD WaitForSingleObject(HANDLE hObject, DWORD dwTimeout);

DWORD WaitForMultipleObjects(DWORD cObjects, LPHANDLE lpHandles, BOOL bWaitAll, DWORD dwTimeout);

Как следует из названия, эти функции предназначены для ожидания одного или нескольких объектов. Однако следует вспомнить примечание, которое я привел к описанию структуры OVERLAPPED! Поэтому не мудрствуя лукаво будем ожидать только объекты event.

Функция WaitForSingleObject ожидает только один объект задаваемый первым параметром. Вторым параметром задается максимальное время ожидания наступления события в миллисекундах. Если вместо времени указана магическая величина INFINITE, то событие будет ожидаться вечно.

Функция WaitForMultipleObjects ждет несколько событий. Первый параметр указывает сколько именно, а второй задает массив дескрипторов этих событий. Замечу, что один и тот же дескриптор нельзя указывать в этом массиве более одного раза. Третий параметр задает тип ожидания. Если он равен TRUE, то ожидается наступление всех событий. Если FALSE, то наступления любого одного из указанных. И естественно тоже можно задать максимальное время ожидания последним параметром.

Если событие наступило, то функции возвращают значения от WAIT_OBJECT_0 до WAIT_OBJECT_0+cObject-1. Естественно, что WaitForSingleObject может вернуть только WAIT_OBJECT_0 (если конечно не произошло ошибки). Если произошла ошибка, то будет возвращено WAIT_FAILED. При превышении максимального времени ожидания функции вернут WAIT_TIMEOUT.

Вернусь к объектам event, которые мы собственно и используем для ожидания. Поясню, почему для наших целей требуются события с ручным сбросом. Функции ReadFile и WriteFile в асинхронном режиме первым делом сбрасывают (переводят в занятое состояние) как дескриптор файла, так и дескриптор объекта event задананный в структуре OVERLAPPED. Когда операция чтения или записи завершается система устанавливает эти дескрипторы в свободное состояние. Тут все логично. Однако и функции WaitForSingleObject и WaitForMultipleObjects для событий с автоматическим сбросом так же выполняют их перевод в занятое состояние при вызове. Для событий с ручным сбросом этого не происходит. Теперь представьте, что операция ввода/вывода завершилась ДО вызова WaitForSingleObject. Представили? Для событий с автоматическим сбросом снова будет выполнен перевод объекта в занятое состояние. Но освобождать то его будет некому! Более подробная информация об объектах event выходит за рамки этой статьи.

Теперь небольшой пример. Все подробности, не относящиеся к работе в асинхронном режиме я опускаю.

#include <windows.h>

#include <string.h>

. . .

HANDLE port;

char* buf;

OVERLAPPED ovr;

DWORD bc;

. . .

port=CreateFile("COM2", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

memset(&ovr, 0, sizeof(ovr));

ovr.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

ReadFile(port, buf, buf_size, &bc, &ovr);

/* Выполняем некую полезную работу */

if (WaitForSingleObject(ovr.hEvent,10000) == WAIT_OBJECT_0) {

 GetOverlappedResult(port, &ovr, &bc, FALSE);

} else {

 /* Обработка ошибки */

}

CloseHandle(port);

CloseHandle(ovr.hEvent);

В этом примере переменная bc, предназначенная для получения количества считанных байт, после вызова ReadFile будет равна 0, так как никакой передачи информации еще не было. После вызова GetOverlappedResult в эту переменную будет помещено число реально считанных байт.

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

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

Теперь, уже совсем кратко, еще об одной возможности, реализованной только в Windows NT. Речь идет о "тревожном вводе-выводе". Эта возможность реализуется функциями ReadFileEx, WriteFileEx и SleepEx. Суть использования данных функий такова. Вы вызываете расширенную функцию записи или чтения, которая имеет еще один параметр – адрес функции завершения. После чего, вызвав расширенную функцию засыпания, освобождаете процессор. После завершения ввода/вывода Ваша функция завершения будет вызвана системой. Причем вызвана ТОЛЬКО в том случае, если ваша программа вызвала SleepEx. Нетрудно заметить, что данный вариант работы подходит для систем с большим количеством портов и работающих в режиме ответа по требованию. Например, сервер с мультипортовым контроллером последовательного порта, к которому подключены модемы.

Теперь, но ОЧЕНЬ кратко, залезем в еще большие дебри. Предположим, что протокол обмена с Вашим устройством, подключенным к последовательному порту, очень сложен (передаются большие и сложные структуры данных). При этом Ваша программа должна получать уже полностью принятую и проверенную информацию. Предположим так же, что Ваша программа занимается очень большими и сложными вычислениями и ей нет времени отвлекаться на обработку ввода/вывода. Да и сложность ее такова, что встраивание фонового ввода/вывода сделает ее трудно прослеживаемой и неустойчивой. Чувствуете, куда я клоню? Правильно, к выделению всех тонкостей ввода/вывода в отдельный поток. Возможно выделение и в отдельную задачу, но в этом случае мы не получим никакой выгоды, а накладные расходы на переключение задач гораздо больше, нежели на переключение потоков в одной задаче.

Потоки создаются функцией CreateThread, и уничтожаются функциями ExitThread и TerminateThread. Принцип работы таков. Вы создаете поток. При этом управление получает Ваша функция потока. Она работает параллельно, как минимум, основному потоку Вашей программы. Функция открывает порт и выполняет все необходимые настройки. Затем она выполняет весь ввод/вывод, при чем совершенно не важно, используется синхронный или асинхронный режим. При засыпании потока (при синхронном режиме) остальные потоки Вашей программы продолжат выплняться. Когда завершится необходимый обмен информацией с устройством и данные будут готовы для передачи основной программе Ваш поток установит некий флаг, котрый будет воспринят основной программой как готовность данных. После их обработки и формирования блока выходной информации основной поток установит другой флаг, который будет воспринят потоком ввода-вывода как готовность данных для передачи. При этом в качестве флагов можно использовать как объекты event, так и обычные переменные (ведь все потоки задачи выполняются в едином адресном прогстранстве). В случае использования обычных глобальных переменных не забудте в их определения добавить модификатор volatile. Он обозначает, что переменная может измениться асинхронно и компилятор не должен строить иллюзий насчет возможности ее оптимизации. В противном случае у Вас ничего не получится. Так как в потоке ввода/вывода, выполняющемся параллельно основному потоку программы, можно использовать асинхронный ввод/вывод, то достаточно просто реализуется возможность обработки большого количества портов. Фактически поток ввода/вывода будет работь еще и параллельно самому себе. При запуске такой задачи на многопроцессорной машине выгода от использования многопоточности будет очевидна, поскольку потоки будут выполняться на разных процессорах.

На этом, пожалуй, следует остановиться. Асинхронные режимы и многозадачность темы отдельных больших статей. Эти статьи будут написаны и выложены на сервер. Информации этой статьи достаточно, что бы Вы смогли уверенно начать работать с портами. Безусловно, не обойтись без чтения подробных описаний на упомянутые здесь функции и структуры. Без детальнейшей проработки, иногда очень изощренных, алгоритмов. Я постарался дать общую картину проблемы и путей ее решения. Насколько это удалось, судить Вам.

Назад 1 ... 5 6 7 8 9 Вперед
Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*