Работа с мьютексами в стиле ООП (Delphi, TMutex)

Механизм мьютексов предназначен для того чтобы обеспечить работу с ресурсом не более одного потока одновременно.

В Delphi существует два способа работы с мьютексами.

  1. Использование «обычных» для этих целей низкоуровневых (WinAPI) функций (CreateMutex и т.д.).
    Данный способ работает практически во всех версиях, давно освоен программистами и не нуждается в дополнительном описании (материалов на эту тему по Delphi в интернете более чем достаточно). Назовём его «классическим способом»;
  2. Использование класса TMutex.
    Этот класс впервые появился в версии XE Он предоставляет простой интерфейс для работы с мьютексами и почти полностью избавляет от необходимости использования низкоуровневых функций при работе с ними.

Рассмотрим второй способ.

Класс TMutex описан в модуле System.SyncObjs. Его конструктор имеет три перегрузки.

Наиболее предпочтительна для создания мьютекса следующая перегрузка:

  • Первый параметр (TSecurityAttributes) — указатель на запись атрибутов защиты (в простейшем случае допускается nil).
  • Второй параметр (Boolean) – определяет, станет ли поток, создавший мьютекс его владельцем;
  • Третий параметр (String) — уникальное имя мьютекса.
  • Четвёртый параметр (Boolean) – способность мьютекса работать с компонентами STA (single-threaded apartment) COM. Это параметр необязательный и по умолчанию имеет значение false.

Вторая перегрузка открывает уже существующий мьютекс по уникальному имени (принимаемые параметры аналогичны предыдущей).

Третья перегрузка создаёт новый мьютекс, но принимает только один параметр, возможность работы с STA COM.

В процессе работы с TMutex в основном используются три его метода:

  • Acquire – захват мьютекса;
  • Release – освобождение мьютекса;
  • WaitFor – ожидание доступа к мьютексу в течение некоторого времени (таймаута). Метод унаследован от предка – класса THandleObject.

Продолжительность таймаута задаётся единственным параметром этого метода. Если требуется ожидать неопределённый период времени, передаётся значение INFINITE.

В отличие от используемой в «классическом способе» функции WaitForSingleObject, данный метод возвращает значение перечисления TWaitResult. Поэтому, сравнить его с WAIT_TIMEOUT напрямую не получится (в прочем это и не нужно).

В предыдущей статье посвящённой мьютексам уже было отмечено, что существуют два основных подхода к их применению.

  • В зависимости от состояния мьютекса соответствующие действия выполняются сразу. Поток, обратившийся к мьютексу, не приостанавливает своей работы;
  • Поток, обратившийся к мьютексу, ожидает его освобождения и только после этого продолжает работу.

Покажем реализацию обоих подходов с использованием TMutex.

Первый подход реализуется следующим образом. Выполняется проверка занятости мьютекса с помощью функции GetLastError и в зависимости от её результатов производятся те или иные действия. Однако добавляются вызовы методов захвата и освобождения мьютекса, что делает код несколько более громоздким.

Мьютекс, занятый с помощью метода Acquire, необходимо впоследствии обязательно освободить с помощью метода Release. Иначе мьютекс так и останется занятым и другой поток не сможет получить к нему доступ.

Реализация второго подхода почти полностью повторяет «классический способ». Только вместо двух параметров (дескриптор и время) используется всего один.

Важно отметить, что в примере выше дескриптор также не используется. Всю «низкоуровневую» работу уже сделали программисты Embarcadero.

По завершении работы с мьютексом, когда в нём уже нет необходимости, он уничтожается стандартным способом.

В отношении работы с TMutex можно отметить следующее.

Достоинства:

  • Простота и удобство работы.
    Нет необходимости манипулировать дескрипторами и низкоуровневыми функциями. Практически весь необходимый функционал собран в одном классе;
  • Более широкие возможности и большая гибкость прикладных программ.
    Так как TMutex это, по сути, «родной» класс Delphi. С его экземпляром можно работать как с обычным объектом.

Недостатки:

  • Некоторые операции требуют дополнительного вызова методов Acquire и Release.
    Может доставить неудобства скорее из-за непривычности;
  • TMutex недоступен в версиях Delphi ниже XE.

В принципе, все перечисленные недостатки можно смело назвать несущественными.

Поэтому не случайно Embarcadero, начиная с версии XE2, настоятельно рекомендует выполнять все операции с мьютексами при помощи этого класса.

Возможность использования «классического способа» по-прежнему остается, так как он основан на WinAPI. Но, при решении прикладных задач он существенно проигрывает TMutex. Поэтому, если используется современная версия Delphi и при этом речь не идёт о разработке системных утилит или решении других подобных задач использование TMutex более предпочтительно.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *