Клас tthread - глава 29

Delphi представляє програмісту повний доступ до можливостей програмування інтерфейсу Win32. Для чого ж тоді фірма Borland представила спеціальний клас для організації потоків? Взагалі кажучи, програміст не зобов'язаний розбиратися у всіх тонкощах механізмів, пропонованих операційною системою. Клас повинен инкапсулировать і спрощувати програмний інтерфейс; клас TThread - прекрасний приклад надання розробнику простого доступу до програмування потоків. Сам API потоків, взагалі кажучи, не дуже складний, але надані класом TThread можливості взагалі чудово прості. У двох словах, все, що вам необхідно зробити, - це перекрити віртуальний метод Execute.

Інша відмінна риса класу TThread - це гарантія безпечної роботи з бібліотекою візуальних компонентів VCL. Без використання класу TThread під час викликів VCL можуть виникнути ситуації, що вимагають спеціальної синхронізації (див. Розд. "Проблеми при синхронізації потоків" далі в цьому розділі).

Потрібно віддавати собі звіт, що з точки зору операційної системи потік - це її об'єкт. При створенні він отримує дескриптор і відстежується ОС. Об'єкт класу TThread - це конструкція Delphi, відповідна потоку ОС. Цей об'єкт VCL створюється до реального виникнення потоку в системі і знищується після його зникнення.

Вивчення класу TThread почнемо з методу Execute:

procedure Execute; virtual; abstract;

Це і є код, виконуваний в створюваному вами потоці TThread.

Хоча формальний опис Execute - метод abstract, але майстер створення нового об'єкта TThread створює для вас порожній шаблон цього методу.

Перевизначаючи метод Execute, ми можемо тим самим закладати в новий потоковий клас то, що буде виконуватися при його запуску. Якщо потік був створений з аргументом CreateSuspended, рівним False, то метод Execute виконується негайно, в іншому випадку Execute виконується після виклику методу Resume (див. Опис конструктора нижче).

Якщо потік розрахований на одноразове виконання будь-яких дій, то ніякого спеціального коду завершення всередині Execute писати не треба.

Якщо ж в потоці виконуватиметься якийсь цикл, і потік повинен завершитися разом з додатком, то умови закінчення циклу повинні бути приблизно такими:

Until CancelCondition or Terminated;

Тут CancelCondition - ваша особиста умова завершення потоку (вичерпання даних, закінчення обчислень, надходження на вхід того чи іншого символу і т. П.), А властивість Terminated повідомляє про завершення потоку (це властивість може бути встановлено як зсередини потоку, так і ззовні; швидше за все, завершується породив його процес).

constructor Create (CreateSuspended: Boolean);

отримує параметр CreateSuspended. Якщо його значення дорівнює True, новостворений потік не починає виконуватися до тих пір, поки не буде зроблений виклик методу Resume. У разі, якщо параметр CreateSuspended має значення False, конструктор завершується і тільки потім потік починає виконання.

destructor Destroy; override;

Деструкція Destroy викликається, коли необхідність в створеному потоці відпадає. Деструкція завершує його і вивільняє всі ресурси, пов'язані з об'єктом TThread. function Terminate: Integer;

Для остаточного завершення потоку (без подальшого запуску) існує метод Terminate. Але якщо ви думаєте, що цей метод робить якісь примусові дії щодо зупинення потоку, ви помиляєтеся. Все, що відбувається, - це установка властивості

property Terminated: Boolean;

в значення True. Таким чином, Terminate - це вказівка ​​потоку завершитися, виражене "в м'якій формі", з можливістю коректно звільнити ресурси. Якщо вам потрібно негайно завершити потік, використовуйте функцію Windows API TerminateThread.

Метод Terminate автоматично викликається і з деструкції об'єкта. Поток- об'єкт VCL чекатиме, поки завершиться поток- об'єкт операційної системи. Таким чином, якщо потік не вміє завершуватися коректно, виклик деструктора потенційно може привести до зависання всієї програми.

Ще одна корисна властивість:

property FreeOnTerminate: Boolean;

Якщо це властивість одно True, то деструктор потоку буде викликаний автоматично по його завершенні. Це дуже зручно для тих випадків, коли ви в своїй програмі не впевнені точно, коли саме завершиться потік, і хочете використовувати його за принципом "вистрілив і забув" (fire and forget).

function WaitFor: Integer;

Метод WaitFor призначений для синхронізації і дозволяє одному потоку дочекатися моменту, коли завершиться інший потік. Якщо ви всередині потоку FirstThread пишіть код

то це означає, що потік FirstThread зупиняється до моменту завершення потоку SecondThread. Метод WaitFor повертає код завершення очікуваного потоку (див. Властивість Returnvalue).

property Handle: THandle read FHandle;

property ThreadID: THandle read FThreadID;

Властивості Handle і ThreadID дають програмісту безпосередній доступ до потоку засобами API Win32. Якщо розробник хоче звернутися до потоку і керувати ним, минаючи можливості класу TThread, значення Handle і ThreadID можуть бути використані в якості аргументів функцій Win32 API. Наприклад, якщо програміст хоче перед продовженням виконання програми дочекатися завершення усіх потоків, він повинен викликати функцію API waitForMuitipieObjects; для її виклику необхідний масив дескрипторів потоків.

property Priority: TThreadPriority;

Властивість Priority дозволяє запитати і встановити пріоритет потоків. Пріоритети потоків в деталях описані вище. Допустимими значеннями пріоритету для об'єктів TThread є tpidle, tpLowest, tpLower, tpNormai, tpHigher, tpHighest і tpTimeCritical.

procedure Synchronize (Method: TThreadMethod);

Цей метод відноситься до секції protected, т. Е. Може бути викликаний тільки з нащадків TThread. Delphi надає програмістові метод Synchronize для

безпечного виклику методів VCL усередині потоків. Щоб уникнути конфліктних ситуацій, метод synchronize дає гарантію, що до кожного об'єкту VCL одночасно має доступ тільки один потік. Аргумент, що передається в метод Synchronize, - це ім'я методу, який виробляє звернення до VCL; виклик Synchronize з цим параметром - це те ж, що і виклик самого методу. Такий метод (класу TThreadMethod) не повинен мати ніяких параметрів і не повинен повертати ніяких значень. Наприклад, в основний формою додатка потрібно передбачити функцію

procedure TMainForm.SyncShowMessage; begin

ShowMessagedntToStr (ThreadListl. Count)); // інші звернення до VCL

а в потоці для показу повідомлення писати не

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

Метод Resume класу TThread викликається, коли потік відновлює виконання після зупинки, або для явного запуску потоку, створеного з параметром CreateSuspended, рівним True.

Виклик методу Suspend припиняє потік з можливістю повторного запуску згодом. Метод suspend припиняє потік незалежно від коду, що виконується потоком в даний момент; виконання триває з точки зупинки.

property Suspended: Boolean;

Властивість suspended дозволяє програмісту визначити, чи не призупинено чи потік. За допомогою цієї властивості можна також запускати і зупиняти потік. Встановивши властивість suspended в значення True, ви отримаєте той же результат, що і при виклику методу Suspend - призупинення. Навпаки, установка властивості Suspended в значення False відновлює виконання потоку, як і виклик методу Resume.

property ReturnValue: Integer;

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

На цьому завершимо детальний огляд класу TThread. Для ближчого знайомства з потоками і класом Delphi TThread створимо багатопотокове додаток. Для цього потрібно написати всього кілька рядків коду і кілька разів клацнути мишею.


Схожі статті