Міністерство освіти України
Національний університет «Львівська політехніка»
Кафедра автоматизованих систем управління
Методичка до лабораторних з ООП
Лабораторна робота №7з курсу
«Об'єктно-орієнтоване програмування»
Львів 2011
Лабораторна робота №7
Створення багатопотокових ужитків
Мета роботи: Вивчення можливостей системи програмування Delphi 7 по створенню багатопотокових ужитків.
Порядок роботи:
Користуючись рекомендованою літературою до лабораторної роботи створити відповідний проект.
Змінити текст програми так, щоб він відповідав індивідуальному завданню.
Оформити звіт для захисту лабораторної роботи за зразком
назва роботи
мета роботи
порядок роботи
короткі теоретичні відомості
алгоритм розв’язку задачі
тексти відповідних модулів проекту
аналіз отриманих результатів та висновки
Питання для самоконтролю
1.Що таке потоки? Де вони використовуються?
2.Як створити модуль для опису потоку в Delphi?
3.Описати основні властивості класу TThread.
4.Описати основні методи класу TThread.
5.Як забезпечити синхронну роботу потоків, пов'язаних між собою?
Теоретичні відомості
Процес - це проста самодостатня програма, яка не викликає інших програм. Процес характеризується своїм адресним простором, віртуальною пам'яттю, виконуваним кодом та даними. В загальному випадку процес може містити декілька потоків.
Потік - це певна послідовність команд, в межах одного процесу, якій операційна система виділяє певні кванти часу.
Потоки виконуються ніби паралельно. Прикладом багатопотокового ужитку є Microsoft Word, який може одночасно друкувати документ, перевіряти орфографію і реагувати на дії користувача, який редагує текст. Отже, якщо в ужитку є незалежні або частково незалежні одна від одної задачі, то доцільно для кожної з них виділити свій потік.
Паралельні потоки працюють в адресному просторі одного процесу і можуть мати доступ до глобальних змінних цього процесу.
Для створення ужитку з декількома потоками використовується компонент TThread - абстрактний клас для створення окремого потоку.
Для включення компонента TThread у проект Delphi використовується пункт меню File\New\ThreadObject. У відповідному вікні слід ввести ім'я дочірнього класу для TThread, наприклад TMyThread. В результаті створюється новий модуль:
unit Unit2;
interface
uses Classes;
type
TMyThread = classs (TThread)
private
protected
procedure Execute; override;
end;
implementation
procedure TMyThread.Execute; { основна процедура потоку}
begin
{код потоку}
end;
end.
Використання потоку завершується по закінченню процедури Execute.
Методи класу потоку.
constructor Create (CreateSuspended: Boolean) - створення об'єкту потоку.
Параметр CreateSuspended задає спосіб виконання потоку. Якщо CreateSuspended = False, то процедура Execute виконується одразу після створення об'єкту. Якщо CreateSuspended = True, то процедура Execute виконується після виконання метода Resume.
Процедура Resume окрім цього може запускати потік, зупинений за допомогою метода Suspend.
Перевірити, чи потік є зупинений, можна за допомогою властивості Suspended.
Якщо у процедурі Execute викликаються методи, або використовуються властивості компонентів VCL, то для уникнення конфлікту між потоками використовується метод Synchronize (Method: ThreadMethod). (Method - це процедура, яка працює з компонентами VCL)/
При цьому код модуля Unit2 зміниться наступним чином.
unit Unit2;
interface
uses Classes; { інші модулі дописує програміст}
type
TMyThread = classs (TThread)
private
procedure Work;
protected
procedure Execute; override;
end;
implementation
procedure TMyThread.Work;
begin
{ код потоку }
end;
procedure TMyThread.Execute; { основна процедура потоку}
begin
Synchronize (Work)
end;
end.
Щоб достроково зупинити виконання потоку у процедуру Execute додається перевірка властивості Terminated. Якщо зовнішній потік викликав метод Terminate для цього потоку, то Terminated встановлюється в True. Наприклад:
procedure TMyThread.Execute;
begin
repeat
DoSomething
until Terminated;
end;
Щоб негайно завершити потік використовується функція Win32 API TerminateThread (Thread1.Handle, 0), де Thread1.Handle - ім'я потоку, 0 - код завершення, що передається у властивість ReturnValue.
По завершенні потоку можливі 2 варіанти:
Об'єкт типу TThread руйнується, зі звільненням пам'яті (якщо властивість FreeOnTerminate = True).
Об'єкт типу TThread залишається у пам'яті (якщо властивість FreeOnTerminate = False). Тоді для звільнення пам'яті використовується метод Free/
Щоб потік очікував на завершення іншого потоку (наприклад, який готує вхідні дані), використовується метод WaitFor. Якщо потік завершений, то цей метод повертає властивість ReturnValue.
Кожен потік має певний рівень пріоритету (властивість Priority): tpIdle (виконується лише, коли система вільна), tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical (найвищий пріоритет).
Приклад. Програма тестування - користувачу задається питання для відповіді за певний час. Використовується потік для висвічування часу, що лишається на відповідь.
Створити дві кнопки - BBegin (пуск, задання питання), BResp (імітація відповіді) і напис Label1 для відображення часу, що залишається.
У модуль головної форми внести наступні зміни:
var TMax: TDateTime;
TMaxSec: word = 10; {10 секунд}
Створити об'єкт-потік із назвою Time, в описі класу якого вводимо
T0 : TDateTime;
У процедурі Execute:
T0 := Now; { поточний момент часу }
repeat
Form1.Label1.Caption := TimeToStr(TMax-(Now-T0));
until Terminated or ((TMax-(Now-T0)) <= 0);
if Terminated then
ShowMessage('Вчасна відповідь');
else
ShowMessage('Час вичерпано ');
В основному модулі введемо глобальну змінну
var Thread : Time; { створення об'єкту класу Time }
В обробник події "натиснення на кнопку BBegin" включити код
TMax := EncodeTime(0,0,TMaxSec,0);
Thread := Time.Create(False); { негайний початок }
Thread.FreeOnTerminate := True;
В обробник події "натиснення на кнопку BResp" включити код
Thread.Terminate;
Після цього зберегти модулі , наприклад, під іменами UTime (модуль класу потоку) та UTime1 (основний модуль). В розділ implementation модуля UTime1 додати оператор Uses UTime, а в розділ implementation модуля UTime - оператор Uses UTime1.
У розділи uses модулів додати SysUtils та Dialogs (для прототипів функцій TimeToStr та ShowMessage).
Для уникнення конфліктів додати метод Synchonize.
unit UTime;
interface
uses Classes; SysUtils, Dialogs;
type
Time = classs (TThread)
private
T0 : TDateTime;
procedure NewCaption;
protected
procedure Execute; override;
end;
implementation
uses UTime1;
procedure Time.NewCaption;
begin
Form1.Label1.Caption := TimeToStr(TMax-(Now-T0));
end;
procedure Time.Execute;
begin
T0 := Now;
repeat
Synchronize (NewCaption)
until Terminated or ((TMax-(Now-T0)) <= 0);
if Terminated then
ShowMessage('Вчасна відповідь');
else
ShowMessage('Час вичерпано ');
end;
end.
Можна додати і другий потік, який підбиває результати тестування та друкує відповідне повідомлення. Для цього слід змінити процедуру Execute наступним чином:
if Terminated then
begin
ShowMessage('Вчасна відповідь');
ReturnValue := 0;
else
begin
ShowMessage('Час вичерпано ');
ReturnValue := 1;
end;
Створити новий клас потоку TResult. У його процедурі Execute записати:
if Thread.WaitFor = 0 then { очікуємо на завершення потоку Thread }
ShowMessage('Тест складено')
else
ShowMessage('Тест не складено');
Thread.Free; { явне руйнування об'єкту Thread при FreeOnTerminate = False }
В основний модуль вносяться наступні зміни:
interface
uses
…., UTime, UResult;
type
TForm1 = class (TForm)
Label1 : TLabel;
BBegin : TButton;
BResp : TButton;
procedure BBeginClick (Sender :TObject);
procedure BRespClick (Sender :TObject);
private
public
end;
var
Form1 : TForm1;
TMax : TDateTime;
TMaxSec : word = 10;
Thread : Time;
Result : TResult;
implementation
{ $R *.DFM}
procedure TForm1.BBeginClick (Sender :TObject);
begin
TMax := EncodeTime(0,0,TMaxSec,0);
Thread := Time.Create(False);
Result := TResult.Create(False);
Result.FreeOnTerminate := True;
end;
procedure TForm1.BBeginClick (Sender :TObject);
begin
Thread.Terminate;
end;
end.
Завдання
Створити проект - модель гри "Арканоїд". Переміщення підставки для перехоплення кульки здійснюється в основному модулі, для відображення руху кульки в просторі використати окремий потік.
Створити проект - модель гри "Стрілялка". Переміщення прицілу для влучення кульки здійснюється в основному модулі, для відображення руху кульки в просторі використати окремий потік.
Створити проект - модель гри "Ловлення рибок". Переміщення вудочки для перехоплення рибки здійснюється в основному модулі, для відображення руху рибки в просторі використати окремий потік.
Створити проект - модель гри "Пакмен". Переміщення кульки-Пакмена здійснюється в основному модулі, для відображення появи маленьких кульок, які з’їдає Пакмен, використати окремий потік.
Створити проект - модель гри "Парашутисти". Переміщення підставки для перехоплення кульки-парашутиста здійснюється в основному модулі, для відображення руху кульки в просторі використати окремий потік.
Створити проект - модель гри "Рухомий хробак". Переміщення хробака здійснюється в основному модулі, для відображення появи маленьких кульок, які з’їдає хробак, використати окремий потік.
Створити проект - модель гри "Бій двох літаків". Переміщення одного літака здійснюється в основному модулі, для відображення руху другого літака використати окремий потік.
Створити проект - модель "Обчислення та малювання". Обчислення здійснюється в основному модулі, для відображення рухомого малюнка використати окремий потік.