На этом шаге мы дадим определение мьютекса и рассмотрим области его использования.
Мъютекс (mutex - mutually exclusive) - это объект синхронизации, который находится в сигнальном состоянии только тогда, когда он не принадлежит ни одному из процессов. Как только хотя бы один процесс запрашивает владение мьютексом, последний переходит в несигналыюе состояние и остается в нем до тех пор, пока не будет освобожден владельцем. Такое поведение позволяет использовать мьютексы для синхронизации совместного доступа нескольких процессов к разделяемому ресурсу. Для создания мьютекса используется функция:
function CreateMutex( lpMutexAttributes: PSecurityAttributes; // Адрес структуры TSecurityAttributes bInitialOwner: BOOL; // Указывает, будет ли процесс владеть мьютексом сразу после создания lpName: PChar // Имя мьютекса ): THandle; stdcall;
Функция возвращает либо идентификатор созданного объекта, либо 0. Если мьютекс с заданным именем уже был создан, возвращается его идентификатор. В этом случае функция GetLastError вернет код ошибки ERROR_ALREDY_EXISTS. Имя не должно совпадать с именем уже существующего объекта типа семафор, событие, задание, таймер ожидания или файл, отображенный на память.
Если неизвестно, существует ли уже мьютекс с таким именем, программа не должна запрашивать владение объектом при создании (то есть должна передать в параметре bInitialOwner значение False).
Если мьютекс уже существует, приложение может получить его идентификатор с помощью функции OpenMutex:
function OpenMutex( dwDesiredAccess: DWORD; // Задает права доступа к объекту bInheritHandle: BOOL; // Задает, может ли объект наследоваться дочерними процессами lpName: PChar // Имя объекта ): THandle; stdcall;
Параметр dwDesiredAccess может принимать одно из следующих значений:
Функция возвращает либо идентификатор мьютекса, установленного в сигнальное состояние, либо 0 в случае ошибки. Мьютекс переходит в сигнальное состояние после срабатывания функции ожидания, в которую был передан его идентификатор. Для возвращения в несигнальное состояние служит функция:
function ReleaseMutex(hMutex: THandle): BOOL; stdcall;
Если несколько процессов обмениваются данными, например, через файл, отображенный на память, каждый из них для корректного доступа к общему ресурсу должен содержать следующий код:
var Mutex: THandle; // При инициализации программы Mutex := CreateMutex(nil, False, 'UniqueMutexName'); if Mutex = 0 then RaiseLastWin32Error; . . . // Доступ к ресурсу WaitForSingleObject(Mutex, INFINITE); try // Доступ к ресурсу, захват мьютекса гарантирует, // что остальные процессы, пытающиеся получить доступ, // будут остановлены на функции WaitForSingleObject . . . finally // Работа с ресурсом окончена, // освобождаем его для остальных процессов ReleaseMutex(Mutex); end; . . . // При завершении программы CloseHandle(Mutex);
Подобный код удобно инкапсулировать в класс, предназначенный для создания защищенного ресурса. Мьютекс имеет свойства и методы для оперирования ресурсом, защищая их при помощи функций синхронизации.
Разумеется, если работа с ресурсом может потребовать значительного времени, то необходимо либо использовать функцию MsgWaitForSingleObject, либо вызывать функцию WaitForSingleObject в цикле с нулевым периодом ожидания, проверяя код возврата. В противном случае приложение окажется "замороженным". Всегда защищайте захват-освобождение объекта синхронизации при помощи блока try...finally, иначе ошибка во время работы с ресурсом приведет к блокированию всех процессов, ожидающих его освобождения.
На следующем шаге мы рассмотрим семафоры.