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

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

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

ьому ядрі дозволяють перемикати режим оптимізації, використовуючи перемикач Application response аплета System панелі управління Windows (мал. 29.1).

До того ж Windows 2000 Professional і Windows 2000 Server мають різні алгоритми виділення квантів часу. Перша клієнтська операційна система виділяє час короткими квантами змінної довжини для прискорення реакції на додатки переднього плану (foreground). Для серверу ж більш важлива стабільна робота системних служб, тому в другій ОС система розподіляє довгі кванти постійної довжини.

 

Мал. 29.1. За допомогою діалогу Performance Options можна управляти алгоритмом призначення пріоритетів

 

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

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

  • потік, що виконується, зупинився для очікування;
  • зявився готовий до виконання потік з вищим пріоритетом.

Тепер, напевно, вам більш ясна небезпека, витікаюча від невиправданого завищення пріоритетів. Адже, якщо є активні потоки з високим пріоритетом, жоден потік з нижчим пріоритетом жодного разу не одержить часу процесора. Ця проблема може підстерігати вас навіть на рівні вашого додатку. Припустимо, ви призначили обчислювальному потоку пріоритет THREAD_PRIORITY_ABOVE_NORMAL, а потоку, де обробляється введення користувача, THREAD_PRIORITY_BELOW_NORMAL. Тоді замість запланованого результату сумістити обчислення з нормальною реакцією додатку ви одержите строго зворотний. Додаток взагалі перестане відгукуватися на введення, і зняти його буде можливо тільки за допомогою засобів ОС.

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

Клас TThread

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 писати не треба.

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

procedure TMyThread. Execute;

begin

repeat

DoSomething;

Until CancelCondition or Terminated;

end;

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

Конструктор обєкту:

constructor Create (CreateSuspended: Boolean);

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

destructor Destroy; override;

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

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