Мьютексы (Mutexes)
Мьютексы (Mutexes)
Критические секции просты в использовании и обладают высоким быстродействием, но не обладают гибкостью в управлении. Нет, например, возможности установить время блокирования или присвоить имя критической секции для того, чтобы два разных процесса могли иметь с ней дело. Оба эти недостатка можно устранить, если использовать такой объект ядра, как mutex. Термин mutex происходит от mutually exclusive (взаимно исключающий). Этот объект обеспечивает исключительный (exclusive) доступ к охраняемому блоку кодов. Например, если несколько процессов должны одновременно работать с одним и тем же связным списком, то на время выполнения каждой операции: добавления, удаления элемента или сортировки, следует заблокировать список и разрешить доступ к нему только одному из процессов.
Для синхронизации потоков разных процессов следует объявить один общедоступный объект класса CMutex, который будет управлять доступом к списку. Мыо-текс предоставляет доступ к объекту любому из потоков, если в данный момент объект не занят, и запоминает текущее состояние объекта. Если объект занят, то мьютекс запрещает доступ. Однако можно подождать освобождения объекта с помощью функции WaitForSingleObject, в которой роль управляющего объекта выполняет тот же мьютекс. Типичная тактика использования такова. Объект
CMutex mutex;
необходимо объявить заранее. Обычно он является членом thread-safe-класса. В точке, где необходимо защитить код, создается объект класса CSingleLock, которому передается ссылка на мьютекс. При попытке включения блокировки вызовом метода Lock надо в качестве параметра указать время (в миллисекундах), в течение которого следует ждать освобождения объекта, охраняемого мьютексом. В течение этого времени либо получим доступ к объекту, либо не получим его. Если объект стал доступен, то мы запираем его от других потоков и производим работу, которая требует синхронизации. После этого освобождаем блокировку. Если время ожидания истекло и доступ к объекту не получен, то обработка этой ситуации (ветвь else) целиком в нашей власти. Если задать ноль в качестве параметра функции Lock, то ожидания не будет. Напротив, можно ждать неопределенно долго, если передать константу INFINITE.
Другой процесс, если он знает, что существует мьютекс с каким-то именем, может сделать этот объект доступным для себя, открыв уже существующий мьютекс. При вызове функции OpenMutex система сканирует существующие объекты-мьютексы, проверяя, нет ли среди них объекта с указанным именем. Обнаружив таковой, она создает описатель объекта, специфичный для данного процесса. Теперь любой поток данного процесса может использовать описатель в целях синхронизации доступа к какому-то коду или объекту. Когда мьютекс становится ненужным, следует освободить его вызовом
CloseHandle(HANDLE hObject);
где hObject — описатель мьютекса. Когда система создает мьютекс, она присваивает ему имя (строка в стиле С). Это имя используется при совместном доступе к мыотексу нескольких процессов. Если несколько потоков создают объект с одним и тем же именем, то только первый вызов приводит к созданию мьютекса. Имя используется при совместном доступе нескольких процессов. Если оно совпадает с именем уже существующего объекта, конструктор создает новый экземпляр класса CMutex, который ссылается на существующий мьютекс с данным именем. Если имя не задано (IpszName равен NULL) мьютекс будет неименованным, и им можно пользоваться только в пределах одного процесса.
С любым объектом ядра сопоставляется счетчик, фиксирующий, сколько раз данный объект передавался во владение потокам. Если поток вызовет, например, CSingleLock: :Lock() ИЛИ WaitForSingleObject () ДЛЯ уже принадлежащего ему объекта, он сразу же получит доступ к защищаемым этим объектом данным, так как система определит, что поток уже владеет этим объектом. При этом счетчик числа пользователей объекта увеличится на 1. Теперь, чтобы перевести объект в свободное состояние, потоку необходимо соответствующее число раз вызвать CSingleLock::Unlock() ffilHReleaseMutex() . Функции EnterCriticalSection и LeaveCriticalSection действуют по отношению к критическим секциям аналогичным образом.
Объект-мьютекс отличается от других синхронизирующих объектов ядра тем, что занявшему его потоку передаются права на владение им. Прочие синхронизирующие объекты могут быть либо свободны, либо заняты и только, а мьютексы способны еще и запоминать, какому потоку они принадлежат. Отказ от мьютекса происходит, когда ожидавший его поток захватывает этот объект, переводя его в занятое состояние, а потом завершается. В таком случае получается, что мьютекс занят и никогда не освободится, поскольку другой поток не сможет этого сделать. Система не допускает подобных ситуаций и, заметив, что произошло, автоматически переводит мьютекс в свободное состояние.