Міністерство освіти і науки України
Національний університет «Львівська політехніка»
Інститут комп’ютерних наук та інформаційних технологій
Кафедра АСУ
Лабораторна робота №7
 Середовище програмування DELPHI
										
Львів 2010
Мета роботи: Вивчення можливостей системи програмування 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.
Завдання
Створити проект - модель гри "Арканоїд". Переміщення підставки для перехоплення кульки здійснюється в основному модулі, для відображення руху кульки в просторі використати окремий потік.
Створити проект - модель гри "Стрілялка". Переміщення прицілу для влучення  кульки здійснюється в основному модулі, для відображення руху кульки в просторі використати окремий потік.
Створити проект - модель гри "Ловлення рибок". Переміщення вудочки для перехоплення рибки здійснюється в основному модулі, для відображення руху рибки в просторі використати окремий потік.
Створити проект - модель гри "Пакмен". Переміщення  кульки-Пакмена здійснюється в основному модулі, для відображення появи маленьких кульок, які з’їдає  Пакмен, використати окремий потік.
Створити проект - модель гри "Парашутисти". Переміщення  підставки для перехоплення кульки-парашутиста здійснюється в основному модулі, для відображення руху кульки в просторі використати окремий потік.
Створити проект - модель гри "Рухомий хробак". Переміщення  хробака здійснюється в основному модулі, для відображення появи маленьких кульок, які з’їдає  хробак, використати окремий потік.
Створити проект - модель гри "Бій двох літаків". Переміщення  одного літака  здійснюється в основному модулі, для відображення руху другого літака використати окремий потік.
Створити проект - модель "Обчислення та малювання". Обчислення здійснюється в основному модулі, для відображення рухомого малюнка використати окремий потік.