Синхронизация потоков в операционной системе Windows, страница 9

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

Листинг 3. Демонстрация работы семафора

#include <windows.h>

#include <iostream.h>

void main()

{

          HANDLE sem=CreateSemaphore(NULL, 3, 3, "nt5bbsem");

          cout<<"Проверить состояние семафора\n";

          cout.flush();

          WaitForSingleObject(sem, INFINITE);

          cout<<"Получен доступ к семафору\n";

          cout.flush();

          MessageBox(NULL, "Получен доступ к семафору", "Semaphore", MB_OK);

          ReleaseSemaphore(sem, 1, NULL);

          CloseHandle(sem);

}

Безопасная синхронизация

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

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

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

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

Использование вызова WaitForMultipleObjects

В некоторых ситуациях поток должен ждать перехода в сигнальное состояние нескольких объектов синхронизации. Для этого удобно использовать вызов WaitForMultipleObjects. В качестве одного из аргументов этот вызов принимает массив дескрипторов объектов синхронизации. Если любой из этих объектов переходит в сигнальное состояние, вызов возвращает вам информацию о том, какой именно объект изменил свое состояние. Как и при использовании других подобных вызовов, вы можете указать период времени, в течение которого следует ожидать изменение состояния объектов. Период времени может быть любым, включая 0 и INFINITE (неограниченное время).