Робота з "потоками" в середовищі Delphi

Информация - Компьютеры, программирование

Другие материалы по предмету Компьютеры, программирование

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

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

Працюючи в Delphi, програміст може також використовувати обєкт типу критична секція (critical section). Критичні секції подібні взаємним виключенням по суті, проте між ними існують дві головні відмінності:

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

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

Візьмемо клас TCriticalSection (модуль SYNCOBJS.PAS). Логіка використовування його проста тримати і не пущать. У багатопотоковому додатку створюється і ініціалізується загальна для всіх потоків критична секція. Коли один з потоків досягає критично важливої ділянки коду, він намагається захопити секцію викликом методу Enter:

MySection. Enter; try DoSomethingCritical;

finally

MySection. Leave;

end;

Коли інші потоки доходять до оператора захоплення секції Enter і знаходять, що вона вже захоплена, вони припиняються аж до звільнення секції першим потоком шляхом виклику методу Leave. Зверніть увагу, що виклик Leave поміщений в конструкцію try..finally тут потрібна стовідсоткова надійність. Критичні секції є системними обєктами і підлягають обовязковому звільненню втім, як і решта обєктів, що розглядаються тут.

Процес. Породження дочірнього процесу

Обєкт типу процес (process) може бути використаний для того, щоб припинити виконання потоку в тому випадку, якщо він для свого продовження потребує завершення процесу. З практичної точки зору така проблема встає, коли потрібно в рамках вашого додатку виконати додаток, створений кимось іншим, або, наприклад, сеанс MS-DOS.

Розглянемо, як, власне, один процес може породити інший. Замість застарілої і підтримуваної тільки для сумісності функції winExec, що перекочувала з колишніх версій Windows, набагато правильніше використовувати могутнішу:

function CreateProcess (IpApplicationName: PChar; IpCorranandLine: PChar;

IpProcessAttributes, IpThreadAttributes: PSecurityAttributes;

blnheritHandles: BOOL;

dwCreationFlags: DWORD; IpEnvironment: Pointer;

IpCurrentDirectory: PChar;

const IpStartupInfo: TStartupInfo;

var IpProcessInformation: TProcessInformation): BOOL;

Перші два параметри ясні це імя додатку, що запускається, і передавані йому в командному рядку параметри. Параметр dwCreationFlags містить прапори, що визначають спосіб створення нового процесу і його майбутній пріоритет. Використані в приведеному нижче лістингу прапори означають: CREATE_NEW_CONSOLE будет запущено новий консольний додаток з окремим вікном; NORMAL_PRIORITY_CLASS нормальний пріоритет.

Структура TStartupInfo містить відомості про розмір, колір, положення вікна створюваного додатку. У нижченаведеному прикладі (лістинг 29.1) використовується поле wshowwindow: встановлений прапор SW_SHOWNORMAL, що означає візуалізацію вікна з нормальним розміром.

На виході функції заповнюється структура IpProcessInformation. У ній програмісту повертаються дескриптори і ідентифікатори створеного процесу і його первинного потоку. Нам знадобиться дескриптор процесу в нашому прикладі створюється консольний додаток, потім відбувається очікування його завершення. Просигналить нам про це саме обєкт IpProcessInformation.hProcess.

Лістинг 29.1. Породження дочірнього процесу

var

IpStartupInfo: TStartupInfo;

IpProcessInformation: TProcessInformation;

begin

FillChar (IpStartupInfo, Sizeof(IpStartupInfo), 10);

IpStartupInfo.cb:= Sizeof(IpStartupInfo);

IpStartupInfo.dwFlags:= STARTFJJSESHOWWINDOW; IpStartupInfo.wShowWindow:= SW_SHOWNORMAL;

if not CreateProcess (nil,

PChar (ping localhost),

nil,

nil,

false,

CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,

nil,

nil,

IpStartupInfo, IpProcessInformation) then

ShowMessage (SysErrorMessage(GetLastError;)

else

begin

WaitForSingleObject

(IpProcessInformation.hProcess, 10000); CloseHandle (IpProcessInformation.hProcess);

end;

end;

Потік

Потік може чекати інший потік точно так, як і інший процес. Очікування можна організувати за допомогою функцій API (як в тільки що розглянутому прикладі), але зручніше це зробити за допомогою методу TThread. WaitFor.

Консольне введення

Консольне введення (console input) годиться для потоків, які повинні чекати відгуку на натиснення користувачем клавіші на клавіатурі. Цей тип очікування може бути використаний в програмі дуплексного звязку (chat). Один потік при цьому чекатиме отримання символів; другий відстежувати введення користувача і потім посилати набраний текст чекаючому додатку.

Сповіщення про зміну у файловій системі

Цей вид обєкту очікування дуже цікавий і незаслужено мало відомий. Ми розглянули практично всі варіанти того, як один потік може подати сигнал іншому. А як одержати сигнал від операційної системи? Ну, наприклад, про те, що у файловій системі відбулися якісь зміни? Такий вид сповіщення з ОС UNIX і доступний програмістам, що працюють з Win32. Для організації моніторинг?/p>