Критична секція

Матеріал з Вікіпедії — вільної енциклопедії.
(Перенаправлено з Критичні секції)
Перейти до навігації Перейти до пошуку

Критична секція (англ. critical section) — об'єкт синхронізації потоків у Windows, що дозволяє запобігти одночасному виконанню деякого критичного набору операцій (зазвичай пов'язаних з доступом до даних) кількома потоками.

Загально[ред. | ред. код]

Об'єкт критичної секції представляє ділянку сирцевого коду, яка в кожен окремий момент може виконуватися лише одним потоком. Критичні секції можна створювати і знищувати, але вони не мають дескрипторів, тому не можуть використовуватись різними процесами. У той же час, для синхронізації потоків одного процесу критичні секції – один з найшвидших і найпростіших способів. Об'єкти критичних секцій не є об'єктами ядра, а розміщуються у користувацькому адресному просторі процесу[1].

В більшості реалізацій з кожною критичною секцією пов'язано лічильник, і захоплення критичної секції можливе лише за нульового значення цього лічильника. Захоплення означає атомарну зміну лічильника на 1. Щоразу, коли потік, що володіє критичною секцією, знов входить до неї, лічильник збільшується на 1, при виході — зменшується. Перехід до чекання на події ядра здійснюється лише у випадку, якщо значення змінної лічильника до спроби захоплення було вже більше 1, тобто критична секція зайнята і є реальне «змагання» двох або більше потоків за ресурс критичної секції.

Аналогічний об'єкт в Linux називається ф'ютекс.

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

Критична секція значною мірою виконує ті ж завдання, що і м'ютекс.

У операційних системах Microsoft Windows головна відмінність між м'ютексом і критичною секцією в тому, що м'ютекс є об'єктом ядра і може бути використаний кількома процесами одночасно, критична секція ж належить процесу і служить для синхронізації тільки його потоків. Процедура входу і виходу критичних секцій зазвичай займає менший час, ніж аналогічні операції м'ютекса.

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

Робота з критичними секціями у Windows[ред. | ред. код]

Для роботи з критичними секціями використовують Windows API.

Перед використання критичної секції її слід ініціалізувати за допомогою процедури InitializeCriticalSection. Її опис мовою C[2]:

void WINAPI InitializeCriticalSection(
 __out  LPCRITICAL_SECTION lpCriticalSection
);

Опис мовою Delphi:

procedure InitializeCriticalSection(
  var lpCriticalSection: TRTLCriticalSection
);

Перед входом у код критичної секції потік має викликати функцію EnterCriticalSection. Її опис мовою C[3]:

void WINAPI EnterCriticalSection(
 __inout  LPCRITICAL_SECTION lpCriticalSection
);

Якщо в цей момент жоден з потоків процесу не володіє критичною секцією, то вона стає власником її об'єкта і продовжує виконання. Якщо секція вже захоплена іншим потоком, викликаючий потік зупиняється до її звільнення. Потік, який володіє критичною секцією, може повторно викликати EnterCriticalSection без блокування свого виконання.

Відразу після закінчення роботи зі спільним ресурсом потік повинен звільнити об'єкт критичної секції викликом процедури LeaveCriticalSection. Її опис мовою C[4]:

void WINAPI LeaveCriticalSection(
 __inout  LPCRITICAL_SECTION lpCriticalSection
);

Опис мовою Delphi:

procedure LeaveCriticalSection(
  var lpCriticalSection: TRTLCriticalSection
);

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

Для кожної критичної секції система підтримує лічильник входжень, тому потік повинна звільнити її стільки разів, скільки захоплювала. Недотримання цієї умови приведе до блокування потоку ("зависання").

Слід також пам'ятати, що спроба виходу з критичної секції, якою потік не володіє, також приведе до блокування викликаючого потоку.

Програма може здійснити спробу захоплення об'єкта критичної секції без зупинки потоку. Для цього використовують функцію TryEnterCriticalSection (опис мовою C[5]):

BOOL WINAPI TryEnterCriticalSection(
 __inout  LPCRITICAL_SECTION lpCriticalSection
);

Опис функції на Delphi:

function TryEnterCriticalSection(
  var lpCriticalSection: TRTLCriticalSection
): BOOL;

У момент виклику вона перевіряє, чи критична секція захоплена якимось іншим потоком. Якщо так — функція повертає false, у протилежному випадку вона захоплює критичну секцію та повертає true.

Після завершення роботи з критичною секцією вона повинна бути вивільнена викликом процедури DeleteCriticalSection. Опис на C[6]:

void WINAPI DeleteCriticalSection(
 __inout  LPCRITICAL_SECTION lpCriticalSection
);

Опис на Delphi:

procedure DeleteCriticalSection(
  var lpCriticalSection: TRTLCriticalSection
);

Приклад використання критичної секції[ред. | ред. код]

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

// Оголошення критичної секції
var ICriSec : TRTLCriticalSection;
...................................
// Ініціалізація - до запуску першого потоку
InitializeCriticalSection(ICriSec);
...................................
// Функція потоку
function ThreadFunc(Ptr: Pointer): LongInt;
var I : integer;
begin
 for I := 1 to 1000000 do begin
   // Якийсь код
   EnterCriticalSection(ICriSec);
   Inc(A);
   LeaveCriticalSection(ICriSec);
   // Якийсь код
 end;
end;
....................................
// Знищення об'єкта критичної секції
DeleteCriticalSection(ICriSec);

Примітки[ред. | ред. код]

  1. а б Коноваленко І.В., Федорів П.С. Системне програмування у Windows з прикладами на Delphi, Т:ТНТУ.- 2012 [Архівовано 8 грудня 2012 у Wayback Machine.].
  2. MSDN: InitializeCriticalSection function. Архів оригіналу за 20 квітня 2012. Процитовано 29 квітня 2012.
  3. MSDN: EnterCriticalSection function. Архів оригіналу за 18 квітня 2012. Процитовано 29 квітня 2012.
  4. MSDN: LeaveCriticalSection function. Архів оригіналу за 30 січня 2012. Процитовано 29 квітня 2012.
  5. MSDN: TryEnterCriticalSection function. Архів оригіналу за 20 квітня 2012. Процитовано 29 квітня 2012.
  6. MSDN: DeleteCriticalSection function. Архів оригіналу за 22 квітня 2012. Процитовано 29 квітня 2012.

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