|
Критические секции просты в использовании и
обладают высоким быстродействием, но не обладают
гибкостью в управлении. Нет, например,
возможности установить время блокирования или
присвоить имя критической секции для того, чтобы
два разных процесса могли иметь с ней дело. Оба
эти недостатка можно устранить, если
использовать такой объект ядра, как 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 действуют по отношению к
критическим секциям аналогичным образом.
Объект-мьютекс
отличается от других синхронизирующих объектов
ядра тем, что занявшему его потоку передаются
права на владение им. Прочие синхронизирующие
объекты могут быть либо свободны, либо заняты и
только, а мьютексы способны еще и запоминать,
какому потоку они принадлежат. Отказ от мьютекса
происходит, когда ожидавший его поток
захватывает этот объект, переводя его в занятое
состояние, а потом завершается. В таком случае
получается, что мьютекс занят и никогда не
освободится, поскольку другой поток не сможет
этого сделать. Система не допускает подобных
ситуаций и, заметив, что произошло,
автоматически переводит мьютекс в свободное
состояние. |