Створення багатопотокових ужитків
Мета роботи: Вивчення можливостей системи програмування Delphi 5 по створенню багатопотокових ужитків.
Порядок роботи:
Користуючись рекомендованою літературою до лабораторної роботи створити відповідний проект.
Змінити текст програми так, щоб він відповідав індивідуальному завданню.
Оформити звіт для захисту лабораторної роботи за зразком
назва роботи
мета роботи
порядок роботи
короткі теоретичні відомості
алгоритм розв’язку задачі
тексти відповідних модулів проекту
аналіз отриманих результатів та висновки
Питання для самоконтролю
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 (найвищий пріоритет).

Завдання
Створити проект - модель гри "Рухомий хробак". Переміщення хробака здійснюється в основному модулі, для відображення появи маленьких кульок, які з’їдає хробак, використати окремий потік.
Текст Програми:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, Buttons, ComCtrls, StdCtrls, jpeg, Unit2;
type
TSegment=class(TImage)
public
Direction:byte;
end;
TSegments=array of TSegment;
type
TForm1 = class(TForm)
Timer1: TTimer;
Image1: TImage;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
function CreateSegment(Pos:TPoint; Dir:byte):TSegment;
procedure AddSegments(Quantity:integer);
procedure FormCreate(Sender: TObject);
procedure MoveSegments(var Segments:TSegments);
procedure ChangeDirection(var Segments:TSegments; Key:Word);
procedure Timer1Timer(Sender: TObject);
procedure FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Snake: TSegments;
T1:TMyThread;
olddir,newdir,home,n:byte;
i:integer;
stop:boolean;
implementation
{$R *.dfm}
{$R Data.res}
procedure TForm1.AddSegments(Quantity:integer);
var
i:integer;
Pos:TPoint;
begin
For i:=1 to Quantity do begin
SetLength(Snake,Length(Snake)+1);
if Length(Snake)=1 then begin
Snake[High(Snake)]:=CreateSegment(Point(0,150),3);
end
else begin
Pos:=Point(Snake[High(Snake)-1].left,Snake[High(Snake)-1].Top);
case Snake[High(Snake)-1].Direction of
1: Snake[High(Snake)]:= CreateSegment(Point(Pos.X+15,Pos.Y),Snake[High(Snake)-1].Direction);
2: Snake[High(Snake)]:= CreateSegment(Point(Pos.X,Pos.Y+15),Snake[High(Snake)-1].Direction);
3: Snake[High(Snake)]:= CreateSegment(Point(Pos.X-15,Pos.Y),Snake[High(Snake)-1].Direction);
4: Snake[High(Snake)]:= CreateSegment(Point(Pos.X,Pos.Y-15),Snake[High(Snake)-1].Direction);
end;
end;
end;
end;
procedure TForm1.MoveSegments(var Segments:TSegments);
var T1:TMyThread;
key:word;
i:integer;
begin
for i:=High(Segments) downto 1 do Segments[i].Direction:=Segments[i-1].Direction;
for i:=0 to High(Segments) do begin
case Segments[i].Direction of
1: Segments[i].Left:=Segments[i].Left-15;
2: Segments[i].Top:=Segments[i].Top-15;
3: Segments[i].Left:=Segments[i].Left+15;
4: Segments[i].Top:=Segments[i].Top+15;
end;
end;
if OldDir<>NewDir then begin
case NewDir of
1: key:=vk_Left;
2: key:=vk_up;
3: key:=vk_right;
4: key:=vk_down;
end;
changedirection(Segments,key);
olddir:=Segments[0].Direction;
end;
if Edit1.Text='' then else
if (Snake[0].Left>= strtoint(Edit1.Text)-10) and (Snake[0].Left<= strtoint(Edit1.Text)+10)and(Snake[0].Top>=strtoint(Edit2.Text)-10) and(Snake[0].Top<=strtoint(Edit2.Text)+10)
then
begin
Edit3.Text:='FFFF';
T1:= TMyThread.Create(false);
T1:= TMyThread.Create(true);
T1.Priority:=tpLower;
T1.Resume;
end;
if (Snake[0].Top>=Form1.ClientHeight)or(Snake[0].Top<=0)or
(Snake[0].Left>=Form1.ClientWidth)or(Snake[0].Left<=0) then
begin
Timer1.Enabled:=false;
for i:=Length(Snake)-1 downto 0 do snake[i].free;
Timer1.Enabled:=true;
SetLength(Snake,0);
AddSegments(2);
end;
end;
function TForm1.CreateSegment(Pos:TPoint; Dir:byte):TSegment;
var
Segment:TSegment;
begin
Segment:=TSegment.Create(Self);
with Segment do begin
Autosize:=true;
Transparent:=true;
Left:=Pos.X;
Top:=Pos.Y;
Direction:=Dir;
Tag:=1;
Picture.Bitmap.LoadFromResourceName(Hinstance,'Segment');
Parent:=Form1;
end;
Result:=Segment;
end;
procedure TForm1.ChangeDirection(var Segments:TSegments; Key:Word);
begin
with Segments[0] do begin
if (Key=vk_Left) and (direction=2) then begin
Left:=Left-15;
Top:=Top+15;
Direction:=1;
end else
if (Key=vk_Left) and (direction=4) then begin
Left:=Left-15;
Top:=Top-15;
Direction:=1;
end else
if (Key=vk_Right) and (direction=2) then begin
Left:=Left+15;
Top:=Top+15;
Direction:=3;
end else
if (Key=vk_Right) and (direction=4) then begin
Left:=Left+15;
Top:=Top-15;
Direction:=3;
end else
if (Key=vk_Down) and (direction=1) then begin
Left:=Left+15;
Top:=Top+15;
Direction:=4;
end else
if (Key=vk_Down) and (direction=3) then begin
Left:=Left-15;
Top:=Top+15;
Direction:=4;
end else
if (Key=vk_up) and (direction=1) then begin
Left:=Left+15;
Top:=Top-15;
Direction:=2;
end else
if (Key=vk_up) and (direction=3) then begin
Left:=Left-15;
Top:=Top-15;
Direction:=2;
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
stop:=false;
DoubleBuffered:=True;
AddSegments(2);
Timer1.Enabled:=true;
T1:= TMyThread.Create(true);
T1.Priority:=tpLower;
T1.Resume;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
MoveSegments(Snake);
if Length(Snake)>=2 then stop:=false;
end;
procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
case key of
vk_left: newdir:=1;
vk_right: newdir:=3;
vk_up: newdir:=2;
vk_down: newdir:=4;
end;
if (Key=VK_ESCAPE) then Application.Terminate;
if (Key=VK_SPACE) then Timer1.Enabled:=not Timer1.Enabled;
if (Key=VK_HOME) then
begin
Timer1.Enabled:=false;
for i:=Length(Snake)-1 downto 0 do snake[i].free;
Timer1.Enabled:=true;
form1.Refresh;
SetLength(Snake,0);
AddSegments(12);
end;
if (Key=VK_Shift) then
begin
Timer1.Enabled:=False;
MessageDlg('"Стрілка вгору" - Рух вгору'+#13+
'"Стрілка вниз" - Рух вниз'+#13+
'"Стрілка вліво" - Рух вліво'+#13+
'"Стрілка вправо" - Рух вправо'+#13+
'"Space" - зупинка або продовження руху'+#13+
'"Home" - повернення в початковий стан'+#13+
'"Insert" - Збільшення розмірів'+#13+
'"Delete" - Зменшення розмірів'+#13+
'"F1" - Збільшення швидкості'+#13+
'"F2" - Зменшення швидкості'+#13+
'"Escape" - вихід з програми',mtInformation,[mbOk],0);
Timer1.Enabled:=true;
end;
if (Key=VK_F1) then
begin
if Timer1.Interval=10 then
else
Timer1.Interval:=Timer1.Interval-20;
end;
if (Key=VK_F2) then Timer1.Interval:=Timer1.Interval+20;
if (Key=VK_Insert) then AddSegments(1);
if (Key=VK_Delete) then
begin
if stop=false then
begin
if Length(Snake)=2 then stop:=true;
Snake[Length(Snake)-1].Free;
SetLength(Snake,Length(Snake)-1);
end;
end;
end;
end.
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, Buttons, ComCtrls, StdCtrls, jpeg;
type
TSegment=class(TImage)
public
Direction:byte;
end;
type
TMyThread = class(TThread)
private
procedure Work;
Public
W:boolean;
protected
procedure Execute; override;
end;
var Plus:TSegment;
implementation
uses Unit1;
procedure TMyThread.Work;
begin
if Form1.Edit3.Text='FFFF' then
begin
Plus.Free;
Form1.Edit3.Text:=''
end
else
begin
randomize;
Plus:=TSegment.Create(Plus);
with Plus do begin
Autosize:=true;
Transparent:=true;
Left:=random(Form1.ClientWidth);
Top:=random(Form1.ClientHeight);
Tag:=1;
Picture.Bitmap.LoadFromResourceName(Hinstance,'Plus');
Parent:=Form1;
end;
Form1.Edit1.Text:=inttostr(Plus.Left);
Form1.Edit2.Text:=inttostr(Plus.Top);
end;
end;
procedure TMyThread.Execute;
begin
Synchronize (Work);
end;
end.

Висновок: Ми вивчили можливості системи програмування Delphi 5 по створенню багатопотокових ужитків.
МІНІСТЕРСВО ОСВІТИ ТА НАУКИ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ „ЛЬВІВСЬКА ПОЛІТЕХНІКА”
Кафедра АСУ
Лабораторна робота №10
на тему:
„Створення багатопотокових ужитків”
Підготував:
студент групи КН-310
Мартинюк Р.В.
Перевірив:
Зварич Р.О.
Львів - 2007