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

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

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

чергу, його класична безвихідь.

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

Головні поняття для розуміння механізмів синхронізації функції очікування і обєкти синхронізації. У Windows API передбачений ряд функцій, що дозволяють припинити виконання потоку, що викликав цю функцію, аж до того моменту, як буде змінений стан якогось обєкту, званого обєктом синхронізації (під цим терміном тут розуміється не обєкт Delphi, а обєкт операційної системи). Проста з цих функцій waitForSingieCbject призначена для очікування одного обєкту.

До можливих варіантів відносяться чотири обєкти, які розроблені спеціально для синхронізації: подія (event), взаємне виключення (mutex), семафор (semaphore) і таймер (timer).

Але окрім спеціальних обєктів можна організувати очікування і інших обєктів, дескриптор яких використовується в основному для інших цілей, але може застосовуватися і для очікування. До них відносяться: процес (process), потік (thread), сповіщення про зміну у файловій системі (change notification) і консольне введення (console input).

Побічно до цієї групи може бути додана критична секція (critical section).

Примітка

Перераховані вище засоби синхронізації в основному інкапсульовані до складу класів Delphi. У програміста є дві альтернативи. З одного боку, до складу бібліотеки VCL включений модуль SYNCOBJS.PAS, що містить класи для події (TEvent) і критичної секції (TCriticalSection). З іншою, з Delphi поставляється відмінний приклад IPCDEMOS, який ілюструє проблеми взаємодії процесів і містить модуль IPCTHRD.PAS з аналогічними класами для тієї ж події, взаємного виключення (TMutex), а також спільно використовуваної памяті (TSharedMem).

Перейдемо до докладного опису обєктів, використовуваних для синхронізації.

Подія

Обєкт типу подія (event) простий вибір для задач синхронізації. Він подібний дверному дзвінку дзвенить до тих пір, поки його кнопка знаходиться в натиснутому стані, сповіщаючи про цей факт оточуючих. Аналогічно, і обєкт може бути в двох станах, а чути його можуть багато потоків відразу. Клас TEvent (модуль SYNCOBJS.PAS) має два методи: setEvent і ResetEvent, які переводять обєкт в активний і пасивний стан відповідно. Конструктор має наступний вигляд:

constructor Create (EventAttributes: PSecurityAttributes;

ManualReset, InitialState: Boolean; const Name: string);

Тут параметр initialstate початковий стан обєкту, ManualReset спосіб його скидання (перекладу в пасивний стан). Якщо цей параметр рівний True, подія повинна бути скинуте уручну. Інакше подія скидається у міру того, як стартує хоч один потік, що чекав даного обєкту.

На третьому методі:

TWaitResult = (wrSignaled, wrTimeout, wrAbandoned, wrError);

function WaitFor (Timeout: DWORD): TWaitResult;

зупинимося докладніше. Він дає можливість чекати активізації події протягом Timeout мілісекунд. Як ви могли здогадатися, усередині цього методу відбувається виклик функції waitFotsingieObject. Типових результатів на виході waitFor два wrsignaied, якщо відбулася активізація події, і wrTimeout, якщо за час тайм-ауту нічого не відбулося.

Примітка

Якщо потрібно (і допустимо!) чекати нескінченно довго, слід встановити параметр Timeout в значення INFINITE.

Розглянемо маленький приклад. Включимо до складу нового проекту обєкт типа TThread, наповнивши його метод Execute наступним вмістом:

Var res: TWaitResult;

procedure TSimpleThread. Execute;

begin

e:= TEvent. Create (nil, True, false, test);

repeat

e. ReSetEvent;

res:= e. WaitFor(10000);

Synchronize(Showlnfo);

until Terminated; e. Free;

end;

procedure TSimpleThread. Showlnfo;

begin

ShowMessage (IntToStr(Integer (res)));

end;

На головній формі розмістимо дві кнопки натиснення однієї з них запускає потік, натиснення другої активізує подію:

procedure TForml. ButtonlClick (Sender: TObject);

begin

TSimpleThread. Create(False);

end;

procedure TForml. Button2Click (Sender: TObject);

begin

e. SetEvent;

end;

Натиснемо першу кнопку. Результат (метод Showlnfo), що тоді зявився на екрані, залежатиме від того, чи була натиснута друга кнопка або закінчилися відведені 10 секунд.

Події використовуються не тільки для роботи з потоками деякі процедури операційної системи автоматично перемикають їх. До числа

таких процедур відносяться відкладене (overlapped) введення/ висновок і події, повязані з комунікаційними портами.

Взаємні виключення

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

Клас, що інкапсулює взаємне виключення, TMutex знаходиться в модулі IPCTHRD.PAS (приклад IPCDEMOS). Конструктор:

constructor Create (const Name: string);

задає імя створюваного обєкту. Спочатку він не належить нікому. (Але функція API createMutex, що викликається в ньому, дозволяє передати створений обєкт тому потоку, в якому це відбулося.) Далі метод

function Get (TimeOut: Integer): Boolean;

виробляє спробу протягом Timeout мілісекунд заволодіти обєктом (в цьому випадку результат рівний True). Якщо обєкт більш не потрібен, слід викликати метод

function Release: Boolean;

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

Семафор

Семафор (semaphore) подібний взаємному виключенню. Різниця між ними у тому, що семафор може управляти кількістю потоків, які мають до нього доступ. Семафор встановлюється на граничне число потоків, яким доступ дозволений. Коли це число досягнуте, подальші потоки будуть припинені, поки один або більш потоків не відєднаються від семафора і не звільнять доступ.

Як приклад використовуванн