Именно поэтому необходим механизм, позволяющий потокам согласовывать свою работу с общими ресурсами. Этот механизм получил название механизма
Структура механизма синхронизации
Что же представляет собой этот механизм? Это набор объектов операционной системы, которые создаются и управляются программно, являются общими для всех потоков в системе (некоторые – для потоков, принадлежащих одному процессу) и используются для координирования доступа к ресурсам. В качестве ресурсов может выступать все, что может быть общим для двух и более потоков – файл на диске, порт, запись в базе данных, объект GDI, и даже глобальная переменная программы (которая может быть доступна из потоков, принадлежащих одному процессу).
Объектов синхронизации существует несколько, самые важные из них – это взаимоисключение (mutex), критическая секция (critical section), событие (event) и семафор (semaphore). Каждый из этих объектов реализует свой способ синхронизации. Какой из них следует использовать в каждом конкретном случае вы поймете, подробно познакомившись с каждым из этих объектов. Также в качестве объектов синхронизации могут использоваться сами процессы и потоки (когда один поток ждет завершения другого потока или процесса); а также файлы, коммуникационные устройства, консольный ввод и уведомления об изменении (к сожалению, освещение этих объектов синхронизации выходит за рамки данной статьи).
В чем смысл объектов синхронизации? Каждый из них может находиться в так называемом
Важно понимать, что никакой реальной связи между объектами синхронизации и ресурсами нет. Они не смогут предотвратить нежелательный доступ к ресурсу, они лишь подсказывают потокам, когда можно работать с ресурсом, а когда нужно подождать. Можно провести грубую аналогию со светофорами – они показывают, когда можно ехать, но ведь в принципе водитель может и не обратить внимания на красный свет (правда, потом он об этом скорее всего пожалеет ;)
Работа с объектами синхронизации
Чтобы создать тот или иной объект синхронизации, производится вызов специальной функции WinAPI типа Create… (напр. CreateMutex). Этот вызов возвращает дескриптор объекта (HANDLE), который может использоваться всеми потоками, принадлежащими данному процессу. Есть возможность получить доступ к объекту синхронизации из другого процесса – либо унаследовав дескриптор этого объекта, либо, что предпочтительнее, воспользовавшись вызовом функции открытия объекта (Open…). После этого вызова процесс получит дескриптор, который в дальнейшем можно использовать для работы с объектом. Объекту, если только он не предназначен для использования внутри одного процесса, обязательно присваивается имя. Имена всех объектов должны быть различны (даже если они разного типа). Нельзя, например, создать событие и семафор с одним и тем же именем.
По имеющемуся дескриптору объекта можно определить его текущее состояние. Это делается с помощью т.н.
Если время ожидания указано равным нулю, функция возвращает результат немедленно, в противном случае она ждет в течение указанного промежутка времени. В случае, если состояние объекта станет сигнальным до истечения этого времени, функция вернет WAIT_OBJECT_0, в противном случае функция вернет WAIT_TIMEOUT.
Если в качестве времени указана символическая константа INFINITE, то функция будет ждать неограниченно долго, пока состояние объекта не станет сигнальным.
Если необходимо узнавать о состоянии сразу нескольких объектов, следует воспользоваться функцией WaitForMultipleObjects.
Чтобы закончить работу с объектом и освободить дескриптор вызывается функция CloseHandle.
Очень важен тот факт, что обращение к ожидающей функции
Теперь давайте рассмотрим каждый тип объектов синхронизации в отдельности.
Взаимоисключения