Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

методички / 4045 ЭИ

.pdf
Скачиваний:
33
Добавлен:
14.05.2019
Размер:
1.16 Mб
Скачать

Приложение 2

type

// Запись для асинхронного обмена данными от потока к интерфейсной панели

TDataVision = Record

FCounter: Integer; // Текущий счетчик процесса FCountTo: Integer; // Конечный счетчик процесса

PctDone: Extended; // Oтносительная позиция процесса между start и end Step: LongInt; // Шаг прогресс-линии

Interval: integer; // Интервал обращения процесса к изменению данных end;

{Класс независимого потока TMyThread}

TMyThread = class(TThread) private

StartTime,EndTime: TDateTime; // Временные интервалы процесса public

DataVision: TDataVision;

Position: extended; // Расчет позиции ProgressBar

btReadOn: boolean;

// Флаг разрешения чтения данных

ID: integer;

// Идентификатор потока

mRep: TMemo;

 

// Отчет процессов по завершению

pause: boolean;

// Приостановка потока (имитация Suspend)

function getPriority(pri: TThreadPriority): string;

Procedure Report;

// Отчет о затраченном времени по завершению

Procedure starTHread; // Запуск процесса

Procedure setPriory(set_: integer); // Установка приоритета constructor Create(Suspended_: Boolean;

id_:integer; priory: integer;

DataVision_: TDataVision; mem_: TMemo);

protected

procedure Execute; override; // Процедура потока

procedure DoWork; // Установка параметров видимых элементов end;

{Класс блокируемой коллекции потоков TCollthreadlist}

TCollthreadlist = class(tthreadlist) private

public

curentVisioItem: integer; // Текущий индекс просматриваемого элемента constructor create;

Destructor Destroy; override;

procedure ClearThread; // Clear коллекции с деструкцией объектов потока end;

implementation

{TCollthreadlist}

constructor TCollthreadlist.Create; begin

inherited Create;

// Счетчик-индекс опроса данных на исходную позицию curentVisioItem := 0;

end;

Destructor TCollthreadlist.Destroy; begin

ClearThread; inherited;// Destroy;

31

setPriory(priory); mRep := mem_;
end;

Приложение 2

end;

procedure TCollthreadlist.ClearThread; var i: integer;

begin

for i:=0 to self.LockList.Count-1 do // Проход по коллекции Tthread(LockList.Items[i]).Destroy; // Освобождение ресурса

self.LockList.Clear;

end;

constructor TMyThread.Create(Suspended_: Boolean; id_:integer; // id процесса priory: integer; // Приоритет

DataVision_: TDataVision; // Обмен mem_: TMemo); // Объект вывода отчета

begin

inherited Create(Suspended_); btReadOn := false;

pause:= Suspended_; DataVision:=DataVision_; id := id_; // идентификатор

{Если FreeOnTerminate имеет значение True, то VCL автоматически разрушает объект потока при его завершении}

FreeOnTerminate := false; // Объект будет разрушаться программно // для получения признака not Assigned()

// Уровень приоритета 0-6

// Memo для формирования отчета

Procedure TMyThread.starTHread; begin

StartTime := Time;

if Suspended then begin

btReadOn := true; // старт + разрешение чтения данных

Start;

end;

pause := false; end;

Procedure TMyThread.setPriory(set_: integer); begin

Priority := CThreadPriority[set_]; // тип приоритета end;

procedure TMyThread.Execute; begin

with DataVision do begin

while (FCounter < FCountTo) and not(self.Terminated) do begin

repeat

if self.Terminated then exit;

if pause then // Эмуляция Suspend := true; sleep(1);

until not pause;

if FCounter mod Interval = 0 then DoWork; // Асинхронный вывод

Inc(FCounter);

end;

EndTime := Time;

32

Приложение 2

//Завершить оформление вывода if not(self.Terminated) then begin

Dec(FCounter);

DoWork; // Завершение вывода данных end;

//Синхронизированный отчет потока

Synchronize(@Report); // о затраченном времени

end;

end;

// Асинхронный вывод данных на интерфейсную панель procedure TMyThread.DoWork;

begin

btReadOn := false; // блокировать чтение данных потока with DataVision do begin

// Изменить данные процесса

PctDone := (FCounter / FCountTo); // относительная часть выполненного Position := Round(Step * PctDone); // задания

end;

btReadOn := true; // Разрешить чтение данных для визуального наблюдения end;

// Отчет по затраченному времени procedure TMyThread.Report; begin

mRep.Append(' ID: '+Format('%.3d',[id])+' Время '+ FormatDateTime('hh:mm:ss:zzz', StartTime-EndTime));

end;

// Пример функции чтения текущего приоритета потока function TMyThread.getPriority(pri: TThreadPriority): string; begin

case pri of

tpTimeCritical: result:='Time Critical'; tpHighest: result:='Highest'; tpHigher: result:='Higher';

tpNormal: result:='Normal'; tpLower: result:='Lower'; tpLowest: result:='Lowest'; tpIdle: result:='Idle';

end;

end;

end.

Модуль классов интерфейсов управления потоком. Асинхронный обмен осуществляет про-

цедура IdleEventHandler(Sender: TObject; var Done: Boolean), которая присваивается системному обработчику события OnIdle на время «простоя» системы. Процедура выполняется с наименьшим приоритетом фона, «просматривает» список потоков и выводит измененные данные на интерфейсную панель. Таким образом, в очереди на вывод визуальных стоит единственный поток «свободного времени», когда как остальные «рабочие» потоки не прерывают на вывод визуальных данных собственные задания.

{АСИНХРОННЫЙ ОБМЕН ДАННЫМИ С VCL; форма интерфейсных элементов потоков (процессов); методы взаимодействия потоков и интерфейсов; управление потоками через интерфейсы}

unit Unit1;

33

Приложение 2

{$mode objfpc}{$H+}

interface

uses

Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls, ExtCtrls, Grids, Spin,unit2;

type

{ TForm1 }

TForm1 = class(TForm) btSaveComponentInStream: TButton; btStart: TButton;

btStop: TButton; GroupBox5: TGroupBox; Label4: TLabel;

Label5: TLabel; LoadComponentInStream: TButton; ComboBox1: TComboBox; GroupBox1: TGroupBox; GroupBox2: TGroupBox; GroupBox3: TGroupBox; GroupBox4: TGroupBox;

Label1: TLabel;

Label2: TLabel;

Label3: TLabel; mfReport: TMemo; Panel1: TPanel; Panel2: TPanel;

ProgressBar1: TProgressBar; ScrollBox1: TScrollBox; SpinEdit1: TSpinEdit; StatusBar1: TStatusBar; StringGrid1: TStringGrid;

procedure btSaveComponentInStreamClick(Sender: TObject); procedure btStartClick(Sender: TObject);

procedure btStopClick(Sender: TObject);

procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure FormCreate(Sender: TObject);

procedure LoadComponentInStreamClick(Sender: TObject); private

//Компоненты управления от интерфейсной панели к потоку procedure GBoxClick(Sender: TObject);

procedure ComboClick(Sender: TObject);

//Фоновый асинхронный обмен от потока к интерфейсной панели procedure IdleEventHandler(Sender: TObject; var Done: Boolean); procedure ShowDataPanel(ind: integer; DataVision_: TDataVision); procedure Start_Idle; // Старт - Фоновый опрос данных вывода потоков procedure Stop_Idle; // Стоп -//-

public

ListProcess: TList; // Список процессов { public declarations }

end;

var

Form1: TForm1;

34

Приложение 2

threadlist:TCollthreadlist;

implementation

{$R *.lfm}

{ TForm1 }

// Передача данных в асинхронном режиме на панель управления procedure TForm1.ShowDataPanel(ind: integer; DataVision_: TDataVision); var GBox: TGroupBox;

begin

//Получить ссылку на интерфейсную панель

GBox := TGroupBox(StringGrid1.Objects[0, ind]);

//Проставить на панели:

//Процент выполнения

(GBox.Controls[2] as TLabel).Caption:=FormatFloat('0.00 %', DataVision_.PctDone * 100); // Статус-линия прогресса

(GBox.Controls[3] as TProgressBar).Position:=Round(DataVision_.Step * DataVision_.PctDone); end;

// Фоновое событие асинхронного опроса измененных данных потока procedure TForm1.IdleEventHandler(Sender: TObject; var Done: Boolean); var

item: TMyThread; // элемент потока из коллекции begin

Label4.Caption := FormatDateTime('hh:mm:ss', Time); // Индикатор активности Idle if not (threadlist.curentVisioItem < threadlist.LockList.Count) then

// Cписок пройден - устанавливаем цикл на начало threadlist.curentVisioItem :=0;

item:= TMyThread(threadlist.LockList.Items[threadlist.curentVisioItem]); if item.btReadOn then begin // Есть разрешение на чтение

ShowDataPanel(threadlist.curentVisioItem, item.DataVision); // Вывод данных item.btReadOn := false; // Не просматривать пока не обновятся данные

end;

Inc(threadlist.curentVisioItem); // Переход на следующий эл-т коллекции Done := false;

end;

procedure TForm1.Start_Idle; // Старт - Фоновый опрос данных вывода потоков begin

// Назначить обработчик событию процесса Idle Application.OnIdle := @IdleEventHandler;

end;

// Стоп Idle

procedure TForm1.Stop_Idle; begin

// Отменить обработку события процесса Idle Application.OnIdle := nil;

Label4.Caption:='---'; end;

// Запись панели управления в файловый поток (создание шаблона интерфейса) procedure TForm1.btSaveComponentInStreamClick(Sender: TObject);

var

Stream: TFileStream ; St: TStringList;

S: String;

35

Приложение 2

begin

Stream := TFileStream.Create( 'CompFile', fmCreate ) ; try

Stream.WriteComponent( GroupBox2 ); Stream.WriteComponent( Label1 ); // ID потока

Stream.WriteComponent( ComboBox1 ); // Управление приоритетом Stream.WriteComponent( Label2 ); // Процент выполнения задания Stream.WriteComponent( ProgressBar1 );// Линия прогресса

St := TStringList.Create; // Строки меню выбора приоритета

St.Add('Высший');

St.Add('Высокий');

St.Add('Выше среднего'); St.Add('Нормальный'); St.Add('Ниже среднего'); St.Add('Низкий');

St.Add('Низший'); S:= St.Text;

Stream.Write(Pointer(S)^, Length(S) * SizeOf(Char)); // Запись формы в файл finally begin

Stream.Free ; St.Free;

ShowMessage('Панель уcпешно сохранена в потоке'); end

end ; end;

// Кнопка старт-стопа выполнения задания процессами procedure TForm1.btStartClick(Sender: TObject);

var i: integer; begin

if (Assigned(ListProcess)) then begin if (ListProcess.Count=0) then

ShowMessage('Процессы еще не загружены!') else

if (TMyThread(ListProcess.Items[0]).pause) then // включаем процессы

begin

for i:=0 to ListProcess.Count-1 do // Эмуляция Suspended := false;

TMyThread(ListProcess.Items[i]).starTHread; StatusBar1.Panels[1].Text := 'Процессы включены'; btStart.Caption:='Стоп';

Start_Idle; // Старт асинхронного вывода end

else

//ShowMessage('Процессы уже включены и работают!') // выключаем процессы

begin

Stop_Idle; // Остановка асинхронного вывода for i:=0 to ListProcess.Count-1 do

// Эмуляция Suspended := true; TMyThread(ListProcess.Items[i]).pause:=true;

StatusBar1.Panels[1].Text := 'Процессы остановлены'; btStart.Caption:='Старт';

end end

else

ShowMessage('Процессы выключены (не выгружены в память)!');

36

Приложение 2

end;

// Остановка-удаление потоков

procedure TForm1.btStopClick(Sender: TObject); begin

if not(Assigned(ListProcess)) then

ShowMessage('Процессы уже выключены!') else

if (ListProcess.Count=0) then ShowMessage('Процессы еще не загружены!')

else

if (TMyThread(ListProcess.Items[0]).Suspended) then

ShowMessage('Процессы еще не запущены по кнопке «Старт»!') else

// выключаем-удаляем процессы begin

Stop_Idle; // Остановка асинхронного вывода // Освобождаем ресурсы от процесса threadlist.ClearThread;

StatusBar1.Panels[1].Text := 'Список процессов удален'; end;

end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin

threadlist.ClearThread; // Удалить коллекцию процессов end;

// Регистрация стандартных компонент procedure TForm1.FormCreate(Sender: TObject); begin

RegisterClass( TGroupBox );// Регистрация компонент, которые

RegisterClass( TLabel ); // сохраняются в файле на форме интерфейса потока

RegisterClass( TProgressBar );

RegisterClass( TComboBox );

RegisterClass( TStringList );

threadlist := TCollthreadlist.Create; // Создать коллекцию потоков end;

// Загрузить цепь процессов и панелей управления (клонировать из файла) procedure TForm1.LoadComponentInStreamClick(Sender: TObject);

var

Stream : TFileStream ; GBox: TGroupBox; Lb1,Lb2: TLabel; CBox: TComboBox; PBar: TProgressBar; St: TStringList;

rect :TRect; i, itm :integer; DataVision: TDataVision;

begin

if threadlist.LockList.Count>0 then begin

ShowMessage('Процессы уже загружены!'); exit;

end;

// Очистить таблицу от интерфейсов предыдущей коллекции if StringGrid1.RowCount > 0 then begin

for i := 0 to StringGrid1.RowCount - 1 do

37

Приложение 2

StringGrid1.Objects[0,i].Destroy; StringGrid1.RowCount := 0;

end; btStart.Caption:='Старт';

ListProcess := threadlist.LockList; // Получить ссылку на коллекцию mfReport.Clear; // размещения потоков

// Подготовить данные визуального отображения with DataVision do begin

FCounter := 0; // Текущий счетчик процесса

Interval := 10000000; // Интервал обращения процесса к изменению данных

FCountTo := round(MAXINT/5); // Конечное число цикла Step := FCountTo div Interval; // Шаг прогресс-линии

end;

// Читать форму интерфейса из потока

Stream := TFileStream.Create( 'CompFile', fmOpenRead ) ; try

Randomize; // Инициализировать генератор случайных чисел for i:=0 to (SpinEdit1.Value-1) do begin

Stream.Seek(0, soBeginning); // Начальная позиция чтения шаблона интерфейса

//Читать: форму бокс-панели из файла-потока

GBox := Stream.ReadComponent( nil ) as TGroupBox ; GBox.Parent := self;

//...обозначение идентификатора потока

Lb1:= Stream.ReadComponent( nil ) as TLabel ; Lb1.Parent := GBox;

// ...выпадающий список выбора приоритета

CBox := Stream.ReadComponent( nil ) as TComboBox ; CBox.Parent := GBox;

// ...процент выполнения

Lb2:= Stream.ReadComponent( nil ) as TLabel ; Lb2.Parent := GBox;

// ...линия прогресса

PBar := Stream.ReadComponent( nil ) as TProgressBar ; PBar.Parent := GBox;

//...строки меню выбора приоритета

St := TStringList.Create; St.LoadFromStream(Stream);

//Строки меню в список выбора

CBox.Items.BeginUpdate; CBox.Items := St; CBox.Items.EndUpdate; CBox.ItemIndex := 0; Lb1.Caption := IntToStr(i+1);

//Назначить события отображения информации при выборе панели интерфейса и

//назначения приоритета потоку

GBox.ControlStyle := [csClickEvents]; GBox.OnClick := @GBoxClick;

(GBox.Controls[1] as TComboBox).OnChange := @ComboClick;

//Запись ID потока на панель интерфейса

GBox.Caption :='Id ' + inttostr(100+i);

itm := 1 + Random(6); // Случайный выбор приоритета

(GBox.Controls[1] as TComboBox).ItemIndex:=itm;

//Назначить интерфейсу владельца - таблицу строк

GBox.Parent := StringGrid1;

//Создать строку в таблице для размещения интерфейса

StringGrid1.RowCount:=StringGrid1.RowCount+1;

38

Приложение 2

//«Прямоугольник» в ячейке таблицы для размещения панели rect:=StringGrid1.CellRect(0,i);

//«Подгон» ячейки под размеры панели

GBox.Top:=rect.Top;

GBox.Left:=rect.Left;

if i = 0 then

StringGrid1.ColWidths[0]:= GBox.Width; // Ширина колонки с панелями

//Высота строки с панелью

StringGrid1.RowHeights[i] := GBox.Height;

//Параметры Линии статуса на панели

//Установки линии прогресса на панели интерфейса

(GBox.Controls[3] as TProgressBar).Max:=DataVision.Step; (GBox.Controls[3] as TProgressBar).Step:=DataVision.Step;

//«Вставить» панель интерфейса в ячейку таблицы

StringGrid1.Objects[0, i] := GBox;

//Создать и добавить поток в коллекцию; DataVision - переменная для обмена

ListProcess.Add(TMyThread.Create(true,i+100,itm,DataVision,mfReport));

end; // for

StatusBar1.Panels[1].Text := 'Процессы загружены'; finally

Stream.Free ; // Освобождение ресурсов файлового потока end ;

end;

// «Щелчок» по панели управления

procedure TForm1.GBoxClick(Sender: TObject); var

Gboxed: TGroupBox absolute Sender; begin

StatusBar1.Panels[1].Text := 'Выбор панели управления процессом: ' + Gboxed.caption; end;

// Изменение приоритета на панели управления procedure TForm1.ComboClick(Sender: TObject); var

TCombo: TComboBox absolute Sender; begin

if threadlist.LockList.Count=0 then

ShowMessage('Процессы удалены и нельзя изменить приоритет!') else

try

//Назначить приоритет по индексу ComboBox,

//используя ссылку коллекции потоков

TMyThread(ListProcess.Items[ strtoint((TCombo.parent.Controls[0] as TLabel).Caption)-1

]).setPriory(TCombo.ItemIndex) ; StatusBar1.Panels[1].Text := 'Изменение приоритета';

//Сообщение об индексе текущего приоритета

StatusBar1.Panels[2].Text := inttostr(ord( TMyThread(ListProcess.Items[

strtoint((TCombo.parent.Controls[0] as TLabel).Caption)-1 ]).Priority

));

finally threadlist.UnlockList;

end;

end;

end.

39

Приложение 3

МНОГОМЕРНЫЕ СВЯЗАННЫЕ ПОТОКИ

П3.1. Описание Основных Классов (Lazarus)

Программное описание класса потока и класса списка, хранящего указатели на экземпляры потока. Ознакомиться с примером, создать собственные экземпляры классов потока и списка (коллекции, цепи) хранения указателей на потоки. Вариант выполняемого задания потоком и его взаимодействие с VCL заявляется студентом, согласуется и утверждается преподавателем. Язык программирования выбирается студентом по желанию.

Модуль классов потока и списка ссылок на поток. Данные потока асинхронно считы-

ваются методом Idle с выводом на интерфейсную панель посредством переменной DataVision. Для запуска потока с панели управления, паузы и продолжения работы потока (по указанию пользователя) объявляется событие evs : TEvent, эмулирующее работу семафора. Кроме того, по завершению процедуры Execute поток запускает соседние процессы (открывает семафоры), находящиеся в ожидании при закрытом семафоре.

{AСИНХРОННЫЙ ОБМЕН ДАННЫМИ С VCL

связанные взаимодействием потоки через открытие/закрытие семафоров; коллекция-лист объектов потока}

unit Unit2;

{$mode objfpc}{$H+}

interface

uses

Classes, SysUtils,Dialogs,STDCTRLS,SyncObjs;

const

CThreadPriority: array[0..6] of TThreadPriority = ( tpTimeCritical, // Приоритет реального времени tpHighest,

tpHigher,

tpNormal, // Приоритет по умолчанию tpLower,

tpLowest,

tpIdle // процесс выполняется только тогда, когда система не занята

);

type

// Запись для асинхронного обмена данными от потока к интерфейсной панели

TDataVision = Record

FCounter: Integer; // Текущий счетчик процесса FCountTo: Integer; // Конечный счетчик процесса

PctDone: Extended; // Oтносительная позиция процесса между start и end Step: LongInt; // Шаг прогресс-линии

Interval: integer; // Интервал обращения процесса к изменению данных

//Коллекция, где регистрируется поток

ParentList: TList;

//Индекс узла коллекции, где регистрируется поток idx: integer;

end;

{Класс независимого потока TMyThread}

TMyThread = class(TThread) private

40

Соседние файлы в папке методички