Шаг 295.
Модели потоков. Объекты синхронизации. Мьютексы

    На этом шаге мы дадим определение мьютекса и рассмотрим области его использования.

    Мъютекс (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, иначе ошибка во время работы с ресурсом приведет к блокированию всех процессов, ожидающих его освобождения.

    На следующем шаге мы рассмотрим семафоры.




Предыдущий шаг Содержание Следующий шаг