Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lab1-2.doc
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
488 Кб
Скачать
☆

Варіант 2 (Порядок виконання лабораторної роботи на мові програмування Delphi)

Створимо модуль для потоку. Для цього виберіть пункт меню File | New | Other для відкриття вікна створення нового модуля. Знайдіть у цьому вікні на вкладці New пункт Thread Object. Виділите його й натисніть кнопку ОК. У результаті з'явиться вікно, показане на рис. 1. У цьому вікні потрібно вказати ім'я потоку який створюється . Назвемо потік - TReadThread. Тепер натисніть кнопку ОК, і Delphi створить модуль-заготовку для нашого майбутнього потоку. Збережете весь проект - головну форму під ім'ям Main, потік під ім'ям MyThread.

New

Рис. 1. Задаєм ім'я потоку

Тепер подивимося на код створеного модуля потоку. Цей код показаний у лістингу 1.

Лістинг 1. Зміст модуля потоку

unit MyThread;

'

interface

uses

Classes;

type

TCountObj = class (TThread)

private

{ Private declarations }

protected

procedure Execute; override;

end;

implementation

{ Important: Methods and properties of objects in VCL can only be used in a

method called using Synchronize, for example,

Synchronize (UpdateCaption) ;

and UpdateCaption could look like,

procedure TCountObj .UpdateCaption;

begin

Forml . Caption := 'Updated in a thread';

end; }

TCountObj }

procedure TCountObj.Execute;

begin

{ Place thread code here }

end;

end.

В об'єкта є тільки одна процедура Execute. У будь-яких потоках ця процедура повинна бути перевизначена, і в ній повинен бути написаний власний код. Це пов'язане з тим, що в об'єкті TThread ця процедура оголошена як абстрактна (abstract) - порожня. Це значить, що процедурі дали ім'я, виділили місце в пам'яті, але її код повинен бути написаний об'єктами нащадками, тобто нами. Метод Execute і є заготовка для коду потоку. Те, що ми напишемо тут, буде виконуватися паралельно основному завданню. Давайте напишемо тут код, наведений у лістингу 2.

Лістинг 2. Код методу Executе

procedure TReadThread.Execute;

begin

FreeOnTerminate:=True;

while not Terminated do

begin

if not ClearCommError(hport, dwError, @ComStat) then

raise Exception.Create('Error clearing port');

dwRead:= ComStat.cbInQue;

if dwRead> 0 then

begin

if not ReadFile(hport, Buf, dwRead, dwRead, @OverRead) then

raise Exception.Create('Error reading port');

Synchronize(DoRead);

end;

end;

end;

У private оголосимо наступні змінні:

ComStat: TComStat;

dwMask, dwError: DWORD;

OverRead: TOverlapped;

Buf: array[0..$FF] of Byte;

dwRead: DWORD;

procedure DoRead;

Останнє, що ми зробимо - підключимо головну форму в розділ uses, тобто запишемо у відповідному місці назву нашої головної форми.

Розглянемо детальніше наш потік.

Опис функцій, властивостей і методів, що використовуються:

Властивість Terminated - якщо true, то потік повинен бути зупинений, інакше потік повинен продовжувати роботу.

Метод Terminate - зупинити виконання потоку.

Властивість FreeOnTerminate - якщо дорівнює true, те по завершенні виконання процедури Execute потік само знищиться.

BOOL ClearCommError(

HANDLE hFile,

LPDWORD lpErrors,

LPCOMSTAT lpStat);

Ця функція скидає ознаку помилки порту (якщо така мала місце) і повертає інформацію про стан порту в структурі COMSTAT

typedef struct _COMSTAT

DWORD fCtsHold:1;

DWORD fDsrHold:1;

DWORD fRlsdHold:1;

DWORD fXoffHold:1;

DWORD fXoffSent:1;

DWORD fEof:1;

DWORD fTxim:1;

DWORD fReserved:25;

DWORD cbInQue;

DWORD cbOutQue;

} COMSTAT, *LPCOMSTAT;

Нас цікавлять тільки 2 поля цієї структури:

  • CbInQue — число символів у прийомному буфері. Ці символи прийняті, але ще не пораховані функцією ReadFile;

  • CbOutQue — число символів у буфері, який маємо передати. Ці символи ще не передані.

Інші поля даної структури містять інформацію про помилки.

У методі Execute запускається цикл while, що буде виконуватися, поки Властивість Terminated не набуде значення true. Усередині циклу ми викликаємо метод synchronize. Як параметр йому передається процедура DoRead (лістинг 3), яка робить вивід у головну форму. Для чого потрібно вставляти процедуру виводу на екран в synchronize? Бібліотека VCL має один недолік - вона не захищена від потоків. Всі користувальницькі компоненти розроблялися так, що до них може одержати доступ тільки один потік. Якщо головна форма й потік спробують одночасно вивести що-небудь у ту саму область екрана або компонент, то програма звалиться. Тому весь висновок на форму потрібно виділяти в окрему процедуру й викликати цю процедуру за допомогою Synchronize. Якщо процедура викликана в методі Synchronize, то до компонентів вікна одержує доступ тільки об'єкт, що викликав метод synchronize. Цей процес непомітний для користувача.

Таким чином, якщо нам потрібно вивести якісь дані з потоку на екран головного вікна, то це потрібно робити в окремій процедурі й викликайте її за ДОПОМОГОЮ Методу Synchronize.

Лістинг 3. Код методу DoRead

procedure TReadThread.DoRead;

var

arrBytes: array of Byte; i: Integer;

begin

SetLength(arrBytes, dwRead);

for i := Low(Buf) to dwRead - 1 do

arrBytes[i] := Buf[i];

for i :=0 to dwRead-1 do

begin

form1.memo1.Text := form1.memo1.Text + inttostr(buf[i]);

//chr(buf[i]);

end;

form1.memo1.Lines.Add('');

end;

Потік готовий. Повертаємось до головної форми. У розділ uses (найпершому, який іде після interface) додамо модуль потоку MyThread. Чому саме в цей розділ, а не той, що розташовано нижче? Це пов'язане з тим, що в розділі private нам потрібно оголосити змінну, що має тип нашого об'єкта. Якщо додати ім'я модуля в другий розділ uses, то він повинен перебувати нижче тої частини коду, де нам потрібно написати оголошення. Саме тому додавати модуль MyThread потрібно в перший розділ uses. У розділі private оголосимо змінну potіk1 типу TReadThread (об'єкт потоку).

Тепер в процедурі відкриття порту TForml.ButtonlClick в кінці допишемо:

potіk1:=TReadThread.create(true);

potіk1.Resume;

potіk1.Priority:=tpLower;

У першому рядку коду ми створюємо потік potіk1. Як параметр може бути true або false. Якщо false, то потік відразу починає виконання, інакше потік створюється, але не запускається. Якщо потік створений не запушеним, то для запуску потрібно використовувати метод Resume, що ми ы робимо в другому рядку. У третьому рядку ми встановлюємо пріоритет потоку зменшений, щоб він не заважав роботі основного потоку й виконувався у фоновому режимі. Якщо встановити пріоритет вищий, то основний потік почне приторможувати, тому що в них будуть однакові пріоритети.

В події onclick кнопки Закрити вкінці напишемо наступний код:

potіk1.Terminate;

Тут відбувається зупинка виконання потоку за допомогою виклику методу Terminate об'єкта потоку. Після виклику цього методу властивість terminated стане рівною true і виконання процедури Execute закінчується.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]