Синхронізація процесів — Вікіпедія

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

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

Засоби синхронізації[ред. | ред. код]

Найпоширеніші засоби синхронізації такі:

Синхронізація у Windows[ред. | ред. код]

Для синхронізації потоків у Windows передбачено ряд функцій синхронізації.

Синхронізація з одним об'єктом[ред. | ред. код]

Найпростішою функцією, яка очікує завершення переходу заданого об’єкта у сигнальний стан, є WaitForSingleObject.

Її опис мовою C[1]:

DWORD WINAPI WaitForSingleObject(  __in  HANDLE hHandle,  __in  DWORD dwMilliseconds ); 

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

function WaitForSingleObject(   hHandle: THandle;         // Дескриптор об’єкта   dwMilliseconds: DWORD     // Час очікування ): DWORD; 

Функція очікує переходу об'єкта з дескриптором hHandle у сигнальний стан протягом dwMilliseconds мілісекунд. Якщо цим параметром передати значення INFINITE, функція буде чекати протягом необмеженого часу. Якщо dwMilliseconds дорівнює 0, то функція перевіряє стан об'єкта й негайно повертає керування. Дескриптор повинен бути відкритий з правом доступу SYNCHRONIZE.

Функція повертає одне з перелічених у таблиці значень.

Значення Опис
WAIT_OBJECT_0 Об'єкт перейшов у сигнальний стан
WAIT_ABANDONED Означає, що заданий об’єкт є м'ютексом, і потік, який володів ним, завершився, не звільнивши його. Власником м'ютекса стає викликаючий потік, а сам м'ютекс переводиться у несигнальний стан
WAIT_TIMEOUT Вийшов час очікування
WAIT_FAILED Відбулася помилка (наприклад, невірне значення hHandle)

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

Тут подано приклад коду, який створює новий процес, запускаючи стандартний текстовий редактор "Блокнот" (Notepad), і чекає на його завершення (приклад подано мовою Delphi)[2].

 var   PI : TProcessInformation;   SI : TStartupInfo;  begin (* ............................ *)  FillChar(SI, Sizeof(SI), #0);  SI.cb := SizeOf(SI);  if not CreateProcess('c:\windows\system32\notepad.exe', nil, nil, nil, false, NORMAL_PRIORITY_CLASS, nil, nil, SI, PI) then     MessageBox(Handle, 'Не вдалося створити процес.', '',        MB_OK or MB_ICONERROR);  WaitForSingleObject(PI.hProcess, INFINITE); 

Синхронізація кількох об'єктів[ред. | ред. код]

Для синхронізації кількох об'єктів використовують фунцію API WaitForMultipleObjects. ЇЇ опис мовою C[3]:

DWORD WINAPI WaitForMultipleObjects(  __in  DWORD nCount,  __in  const HANDLE *lpHandles,  __in  BOOL bWaitAll,  __in  DWORD dwMilliseconds ); 

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

function WaitForMultipleObjects(  nCount: DWORD;              // Кількість об’єктів  lpHandles: PWOHandleArray;  // Адреса масиву об’єктів  bWaitAll: BOOL;             // Чи чекати всі об’єкти  dwMilliseconds: DWORD       // Період очікування ): DWORD; 

Вказівник lpHandles посилається на масив, який містить дескриптори об’єктів очікування. Параметр nCount задає кількість елементів цього масиву. Якщо параметру bWaitAll задати значення true, функція очікуватиме на перехід у сигнальний стан усіх перелічених у масиві lpHandles об’єктів. Коли цей параметр дорівнює false, функція чекатиме на сигнальний стан одного з них. Параметр dwMilliseconds задає час очікування (як для функції WaitForSingleObject). Дескриптори повинні мати право доступу SYNCHRONIZE. Функція повертає одне з перелічених у таблиці значень.

Значення Опис
Число в діапазоні від WAIT_OBJECT_0 до WAIT_OBJECT_0 + nCount – 1 Якщо bWaitAll дорівнює true, то це число означає, що всі об’єкти перейшли у сигнальний стан. Якщо false, то віднявши від отриманого значення WAIT_OBJECT_0, отримаємо індекс елемента масиву lpHandles, який перейшов у сигнальний стан
Число в діапазоні від WAIT_ABANDONED_0 до WAIT_ABANDONED_0 + nCount – 1 Якщо bWaitAll дорівнює true, то це означає, що всі об’єкти перейшли у сигнальний стан, але хоча б один з потоків, який володів ними, завершився, залишивши м'ютекс. Якщо false, то віднявши від результату значення WAIT_ABANDONED_0, отримаємо індекс елемента масиву lpHandles, який відповідає залишеному м'ютексу. Власником м'ютекса стає викликаючий потік, а сам м'ютекс переводиться у несигнальний стан
WAIT_TIMEOUT Вийшов час очікування
WAIT_FAILED Сталася помилка

Приклад очікування кількох об'єктів[ред. | ред. код]

Очікування на завершення роботи трьох потоків з відомими дескрипторами (hThreadX) можна реалізувати так (код мовою Delphi)[2]:

var  Handles : array[0..2] of THandle; ............................................ Handles[0] := hThread1; Handles[1] := hThread2; Handles[2] := hThread3; WaitForMultipleObjects(3, @Handles, true, INFINITE); 

Очікування з обробкою повідомлень[ред. | ред. код]

Функції WaitForSingleObject та WaitForMultipleObjects повністю зупиняють роботу викликаючого потоку, включаючи й стандартну обробку повідомлень графічної підсистеми Windows, тому програма не може навіть перемалювати своє вікно.

Тому функції WaitForSingleObject та WaitForMultipleObjects варто використовувати, коли час очікування невеликий. Якщо ж затримка значна, слід дати можливість програмі обробляти деякі системні повідомлення. Для цього можна використати функцію MsgWaitForMultipleObjects. Її опис мовою C[4]:

DWORD WINAPI MsgWaitForMultipleObjects(  __in  DWORD nCount,  __in  const HANDLE *pHandles,  __in  BOOL bWaitAll,  __in  DWORD dwMilliseconds,  __in  DWORD dwWakeMask ); 

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

function MsgWaitForMultipleObjects(  nCount: DWORD;     // Кількість об’єктів синхронізації  var pHandles;      // Адреса масиву об’єктів  fWaitAll: BOOL;    // Чи чекати на всі об’єкти  dwMilliseconds,    // Період очікування  dwWakeMask: DWORD  // Тип події, яка перериває очікування ): DWORD; 

Основна відмінність цієї функції від функції WaitForMultipleObjects – параметр dwWakeMask, який є комбінацією бітових прапорів QS_XXX і задає типи повідомлень, які перериватимуть очікування незалежно від стану очікуваних об'єктів. Наприклад, маска QS_KEY дозволяє перервати очікування з появою в черзі повідомлень від клавіатури, маска QS_MOUSE забезпечує реакцію на повідомлення миші, а маска QS_PAINT – повідомлення перемальовування WM_PAINT. З появою в черзі викликаючого потоку повідомлень, які відповідають заданій масці, функція повертає значення WAIT_OBJECT_0 + nCount. Отримавши таке значення, програма може обробити його й знову викликати функцію очікування.

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

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