Робота з "потоками" в середовищі Delphi
Информация - Компьютеры, программирование
Другие материалы по предмету Компьютеры, программирование
? файлової системи потрібно використовувати
Три функції FindFirstChangeNotification, FindNextChangeNotification і FinddoseChangeNotification. Перша з них повертає дескриптор обєкту файлового сповіщення, який можна передати у функцію очікування. Обєкт активізується тоді, коли в заданій теці відбулися ті або інші зміни (створення або знищення файлу або теки, зміна прав доступу і т.д.). Друга готує обєкт до реакції на наступну зміну. Нарешті, за допомогою третьої функції слід закрити той, що став непотрібним обєкт.
Так може виглядати код методу Execute потоку, створеного для моніторингу файлової системи:
var DirName: string;
…
procedure TSimpleThread. Execute;
var r: Cardinal;
fn: THandle;
begin
fn:= FindFirstChangeNotification (pChar(DirName), True,
FILEJTOTIFY_CHANGE_FILE_NAME);
repeat
r:= WaitForSingleObject (fn, 2000);
if r = WAIT_OBOECT_0 then
Synchronize (Forml. UpdateList);
if not FindNextChangeNotification(fn) then
break;
until Terminated;
FindCloseChangeNotification(fn);
end;
На головній формі повинні знаходитися компоненти, потрібні для вибору обстежуваної теки, а також компонент TListBox, в який записуватимуться імена файлів:
procedure TForml. ButtonlClick (Sender: TObject);
var dir: string; begin
if SelectDirectory (dir, [], 0)
then begin
Editl. Text:= dir; DirName:= dir;
end;
end;
procedure TForml. UpdateList;
var SearchRec: TSearchRec;
begin
ListBoxl. Clear;
FindFirst (Editl. Text+\*.*, faAnyFile, SearchRec); repeat ListBoxl. Items. Add (SearchRec. Name);
until FindNext(SearchRec)<> 0;
FindClose(SearchRec);
end;
Додаток готовий. Щоб воно стало повнофункціональним, передбачте в ньому механізм перезапуску потоку при зміні обстежуваної теки.
Локальні дані потоку
Цікава проблема виникає, якщо в додатку буде декілька однакових потоків. Як уникнути сумісного використовування одних і тих же змінних декількома потоками? Перше, що спадає на думку, додати і використати поля обєкту нащадка TThread, які можна додати при його створенні. Кожен потік відповідає окремому екземпляру обєкту, і їх дані перетинатися не будуть. (До речі, це одна з великих зручностей використовування класу TThread.) Але є функції API, які знать не знають про обєкти Delphi і їх поля і властивості. Для підтримки розділення даних між потоками на нижньому рівні в мову Object Pascal введена спеціальна директива threadvar, яка відрізняється від директиви опису змінних var тим, що застосовується тільки до локальних даних потоку. Наступний опис:
Var
datal: Integer; threadvar
data2: Integer;
означає, що змінна datal використовуватиметься всіма потоками даного додатку, а змінна data2 буде у кожного потоку своя.
Як уникнути одночасного запуску двох копій одного додатку
Така задача виникає дуже часто. Багато, що особливо починають, користувачів не цілком розуміють, що між клацанням по значку додатку і його запуском може пройти декілька секунд, а то і десятків секунд. Вони починають клацати по значку, запускаючи всі нові копії. Тим часом, при роботі з базами даних і в багатьох інших випадках мати більше однієї копії не тільки не потрібно, але і шкідливо.
Ідея полягає в тому, щоб перша створювана копія додатку захоплювала якийсь, ресурс, а все подальші при запуску намагалися зробити те ж саме і у разі невдачі завершувалися.
Приклад такого ресурсу загальний блок у файлі, що відображається в память. Оскільки цей ресурс має імя, можна зробити його унікальним саме для вашого додатку:
var UniqueMapping: THandle;
FirstWindow: THandle
; begin
UniqueMapping:= CreateFileMapping ($ffffffff,
nil, PAGE_READONLY, 0, 32,MyMap);
if UniqueMapping = 0 then
begin
ShowMessage (SysErrorMessage(GetLastError));
Halt;
end
else if GetLastError = ERROR_ALREADY_EXISTS then
begin
FirstWindow:= FindWindowEx (0, 0, TfmMain. ClassName, nil);
if FirstWindowoO then
SetForegroundWindow(FirstWindow);
Halt;
end;
// Немає інших копій продовження Application. Initialize;
Приблизно такі рядки потрібно вставити в початок тексту проекту до створення форм. Блок спільно використовуваної памяті виділяється в системному сторінковому файлі (про це говорить перший параметр, рівний -1, див. опис функції CreateFileMapping). Його імя муМар. Якщо при створенні блоку буде одержаний код помилки ERROR_ALREADY__EXISTS, це свідчить про наявність працюючої копії додатку. В цьому випадку додаток перемикає фокус на головну форму іншого екземпляра і завершується; інакше процес ініціалізації продовжується.
Потоки, як і інші могутні інструменти, повинні бути використані з обережністю і без зловживань, оскільки можуть виникнути помилки, які дуже важко знайти. Є дуже багато доводів за використовування потоків, але є і доводи проти цього. Робота з потоками буде простішим, якщо враховувати нижче приведені положення.
- Якщо потоки працюють тільки із змінними, оголошеними усередині їх власного класу, то ситуації гонок і безвиході украй маловірогідні.
Іншими словами, уникайте використовування в потоках глобальних змінних і змінних інших обєктів.
- Якщо ви звертаєтеся до полів або методів обєктів VCL, робіть це тільки за допомогою методу Synchronize.
- Не пересинхронізіруйте ваш додаток, а не те воно працюватиме як один єдиний потік. Надмірно синхронізований додаток втрачає всі переваги від наявності декількох потоків, оскільки вони постійно зупинятимуться і чекатимуть синхронізації. Потоки надають витончене рішення деяких сьогоднішніх проблем програмування; але вони також ускладнюють і без того непростий процес відладки. Та все ж переваги потоків однозначно переважують їх недоліки.