М'ю́текс (англ. mutex, від англ. mutual exclusion — взаємне виключення) призначено для захисту певного об’єкта у потоці від доступу інших потоків. М'ютекс є одним із засобів синхронізації роботи потоків або процесів[1].

Загально ред.

М'ютекси є одним з варіантів семафорних механізмів для організації взаємного виключення. Вони реалізовані в багатьох ОС, їхнє основне призначення — організація взаємного виключення для потоків з одного і того ж або різних процесів.

М'ютекси — це прості двійкові семафори, які можуть перебувати в одному з двох станів - сигнальному або несигнальному (відкритий і закритий відповідно). Коли потік отримує м'ютекс, той переводиться в несигнальний стан.

Організація послідовного доступу до ресурсів з використанням м'ютексів стає нескладною, оскільки в кожен конкретний момент тільки один потік може володіти цим об'єктом. Для того, щоб об'єкт mutex став доступний потокам, що належать різним процесам, при створенні йому необхідно присвоїти ім'я. Потім це ім'я потрібно передати «у спадок» завданням, які повинні його використовувати для взаємодії. Для цього вводяться спеціальні системні виклики (наприклад CreateMutex у Windows), в яких указується початкове значення м'ютекса і його ім'я. Для роботи з м'ютексом є кілька функцій. Крім вже згаданої функції створення такого об'єкта (CreateMutex), є функції відкриття (OpenMutex) і функція звільнення цього об'єкта (ReleaseMutex). Конкретні звернення до цих функцій і переліки передаваних і отримуваних параметрів потрібно дивитися в документації на відповідну ОС.

Порівняння м'ютексів та критичних секцій ред.

Порівняно з критичною секцією його функціональність дещо розширена. М’ютекси можуть мати імена і дескриптори. Вони є об’єктами ядра (в той час як критичні секції належать процесу), тому їх можна використовувати для синхронізації потоків із різних процесів. Наприклад, два процеси, які розділяють спільну пам’ять через відображення файлів, можуть використовувати м’ютекс для синхронізації доступу до неї. Так як м’ютекси є об’єктами ядра і мають складнішу реалізацію, на їх обробку, як правило, витрачається більше часу порівняно з критичними секціями. Тому, якщо потрібно синхронізувати роботу потоків одного процесу, варто користуватися критичними секціями.

Робота з м'ютексами у Windows ред.

М’ютекс перебуває в сигнальному стані тільки тоді, коли він не належить жодному із процесів. Як тільки хоча б один процес запитує володіння м’ютексом, він переходить у несигнальний стан і залишається в ньому доти, поки не буде звільнений власником. На відміну від критичних секцій, м’ютекси дозволяють задавати точний інтервал очікування, а м’ютекси, залишені закінченими процесами, автоматично переходять у сигнальний стан. Для створення м’ютекса використовується функція CreateMutex. Її опис мовою C[2]:

HANDLE WINAPI CreateMutex(
 __in_opt  LPSECURITY_ATTRIBUTES lpMutexAttributes,
 __in      BOOL bInitialOwner,
 __in_opt  LPCTSTR lpName
) 

Опис мовою Delphi[1]:

function CreateMutex(
 lpMutexAttributes: PSecurityAttributes;  // Атрибути доступу 
 bInitialOwner: BOOL;  // Задає, чи буде процес володіти
                       // м’ютексом відразу після створення
 lpName: PChar         // Ім'я м’ютекса
): THandle 

Функція повертає дескриптор створеного м’ютекса (або 0 при невдалому завершенні). Якщо м’ютекс із заданим ім'ям вже існує, повертається його дескриптор. У цьому випадку функція API GetLastError поверне код помилки ERROR_ALREDY_EXISTS. Ім'я не повинно збігатися з ім'ям уже існуючого об'єкта типу Semaphore, Event, Job, Waitable Timer або File Mapping. Ім’я може мати префікс "Global\" або "Local\", які відповідно вказують на створення об’єкта у глобальному або сесійному просторі імен. Якщо невідомо, чи існує вже м’ютекс із заданим ім'ям, програма не повинна запитувати володіння об'єктом при створенні (тобто повинна передати значення false як bInitialOwner).

Якщо м’ютекс уже існує, програма може отримати його дескриптор функцією OpenMutex. Її опис на C[3]:

HANDLE WINAPI OpenMutex(
 __in  DWORD dwDesiredAccess,
 __in  BOOL bInheritHandle,
 __in  LPCTSTR lpName
) 

Опис на Delphi[1]:

function OpenMutex(
 dwDesiredAccess: DWORD;  // Задає права доступу до об'єкта
 bInheritHandle: BOOL;    // Задає, чи може об'єкт успадковуватися 
                          // дочірніми процесами
 lpName: PChar            // Ім'я м’ютекса
): THandle 

Параметр dwDesiredAccess може набувати одне з таких значень:

  • MUTEX_ALL_ACCESS – програма отримує повний доступ до об'єкта;
  • MUTEX_MODIFY_STATE – право змінювати стан м’ютекса (потрібне для звільнення м’ютекса функцією ReleaseMutex);
  • SYNCHRONIZE – програма може використовувати об'єкт тільки у функціях очікування й функції ReleaseMutex.

Функція повертає дескриптор відкритого м’ютекса, або 0 у випадку помилки.

Потік захоплює володіння м’ютексом через виклик однієї з функцій очікування (WaitForSingleObject, WaitForMultipleObjects тощо) відносно дескриптора м’ютекса.

Вивільняється м’ютекс за допомогою функції ReleaseMutex. Її визначення на C[4]:

BOOL WINAPI ReleaseMutex(__in  HANDLE hMutex);

І на Delphi[1]:

function ReleaseMutex(hMutex: THandle): BOOL;

Приклад використання м'ютекса ред.

Нижче приведено фрагмент коду потоку[1] (мовою Delphi), який збільшує на 1 спільну для кількох потоків змінну А. Приклад передбачає, що існує кілька потоків, які виконують таку ж дію. Перед звертанням до спільної змінної потік очікує на звільнення м'ютекса. Якщо такої синхронізації не використовувати, то виникатиме конкуренція потоків.

const MutexName = 'IMut';
..............................
function ThreadFunc(Ptr: Pointer): LongInt;
var I : integer;
   hIMut : THandle;
begin
 for I := 1 to 1000000 do begin
   // Якийсь код
   hIMut := CreateMutex(0, false, MutexName);
   WaitForSingleObject(hIMut, INFINITE);
   Inc(A);
   ReleaseMutex(hIMut);
   // Якийсь код
 end;
end;

Примітки ред.

  1. а б в г д Коноваленко І.В., Федорів П.С. Системне програмування у Windows з прикладами на Delphi, Т:ТНТУ.- 2012 [Архівовано 8 грудня 2012 у Wayback Machine.].
  2. MSDN: CreateMutex function. Архів оригіналу за 18 квітня 2012. Процитовано 14 травня 2012.
  3. MSDN: OpenMutex function. Архів оригіналу за 21 квітня 2012. Процитовано 14 травня 2012.
  4. MSDN: ReleaseMutex function. Архів оригіналу за 20 квітня 2012. Процитовано 14 травня 2012.

Див. також ред.