Synchronize в delphi

При використанні в додатку декількох потоків необхідно гарантувати, що в даний момент тільки один з потоків може мати доступ до властивостей і методів об'єкта VCL - візуального компонента Delphi, тобто дії потоків необхідно синхронізувати між собою. Для виконання такої синхронізації в Delphi застосовується спеціальний метод Synchronize. в рамках якого і потрібно викликати процедури, що модифікують властивості візуальних компонентів.

Процедура Synchronize використовує в якості параметра ті процедури, в яких відбувається модифікація властивостей візуальних компонентів, і блокує одночасний доступ до компоненту декількох потоків. Ось який приклад, зокрема, міститься в модулі, створеному Майстром створення потоку:

procedure MyThread.Execute;
begin
Synchronize (UpdateCaption);
end;

В даному випадку потік використовується для зміни заголовка Форми. Зміна заголовка відбувається в процедурі UpdateCaption. Здавалося б, для зміни заголовка цю процедуру досить викликати в основний процедурі потоку, Execute. Однак, якщо кілька таких потоків в програмі одночасно спробують змінити заголовок Форми, то це може привести до непередбачуваних наслідків. Для виключення цього процедура UpdateCaption викликається в процедурі Execute як параметр методу Synchronize.

Потрібно знати, що метод Synchronize виконується в головному потоці програми. Тому, працюючи з декількома потоками в додатку і застосовуючи метод Synchronize, потрібно враховувати, що:
  • по-перше, частий виклик Synchronize гальмує виконання програми;
  • по-друге, якщо практично всі процедури виконується потоку виконуються з використанням методу Synchronize, то сенсу в створенні такого потоку немає - все одно його робота пройде в головному потоці.
Як приклад розглянемо все ту ж модифікацію заголовка Форми. Нехай в одному з потоків відбувається робота з великим масивом, і потрібно відображати який обсяг масиву вже оброблений. Для цього організуємо потік, який буде виконувати цю роботу. Будемо виводити в заголовок Форми індекс елемента, з яким обробляє потік працює в даний момент. Робити це будемо з періодичністю 10 разів в секунду. Спочатку зробимо так:

procedure TMyThread.UpdateCaption;
begin
while True do
begin
Form1.Caption: = IntToStr (I); // I - глобальна змінна основної програми, індекс масиву
sleep (100);
end;
end;

procedure TMyThread.Execute;
begin
Synchronize (UpdateCaption);
end;

Бачимо, що відбувається саме те, про що написано вище. Так як весь код потоку, і модифікація заголовка Форми, і цикл очікування, виконується в методі Synchronize. а значить в головному потоці, то додаток буде виглядати завислим, і його навіть буде неможливо коректно завершити.
Тепер спробуємо вивести цикл за межі Synchronize:

procedure TMyThread.UpdateCaption;
begin
Form1.Caption: = IntToStr (Cap);
end;

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

Деяким об'єктам VCL процедура Synchronize не потрібно, так як вони все ж вміють коректно працювати з потоками, або потребують інших методах синхронізації. Так, коректно працюють з потоками
  • компоненти доступу до баз даних (з використанням компонентів класу TSession). Виняток становлять бази даних Microsoft Access;
  • класи, які працюють безпосередньо з графікою. Це TFont, TPen, TBrush, TBitmap, TMetafile і TIcon. Канву об'єктів цих класів (Canvas) можна використовувати не застосовуючи метод Synchronize. Це робиться за допомогою блокування канви. Тобто, потік, який використовує в даний момент канву, попередньо блокує канву методом Lock, що перешкоджає іншим потокам працювати з канвою, і розблокує потім методом UnLock.
Крім візуальних компонентів, також не вміють працювати з потоками списки TList. Тому в потоках слід використовувати об'єкти TThreadList, які мають методи блокування і розблокування LockList і UnLockList.

Схожі статті