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

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

Подробнее о синхронизации

Основные объекты, предназначенные для синхронизации (события, мьютексы и семафоры), во многом схожи.

Чтобы создать тот или иной объект синхронизации, поток чаще всего обращается к специальной функции (например, вызов CreateEvent создает событие). Если вы работаете с потоками одного процесса, вы можете не именовать объект синхронизации. Если же вы намерены обеспечить доступ к объекту из нескольких разных процессов, вы должны присвоить этому объекту некоторое имя. При подборе имени необходимо учитывать регистр символов, а также нельзя использовать символ обратной косой (\). Имена объектов синхронизации разных типов принадлежат к одному пространству имен. Это значит, что вы не можете назвать событие именем Shabash, а позже присвоить это же имя семафору. Событие и семафор должны обладать отличающимися именами. Если вы не присвоили объекту имя, другие процессы смогут обратиться к этому объекту только при помощи наследования.

Вызов, создающий объект, возвращает дескриптор этого объекта. Потоки, принадлежащие одному процессу, могут использовать один и тот же дескриптор.

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

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

Если у вас уже есть дескриптор (полученный либо при помощи вызова создания, либо при помощи вызова открытия, либо при помощи наследования), вы можете определить, находится ли объект в сигнальном состоянии (signaled state). Для каждого типа объектов термин «сигнальное состояние» имеет разный смысл. Событие находится в сигнальном состоянии в случае, если соответствующий флаг установлен. Мьютекс находится в сигнальном состоянии в случае, если он никому не принадлежит (установив это, ваш поток может стать владельцем мьютекса). Семафор находится в сигнальном состоянии, если его счетчик больше нуля.

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

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