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

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

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

Приложение 2

constructor TMyThread.Create(Suspended_: Boolean; id_:integer; priory: integer;

progbar_: TProgressBar; mesLabel_: TLabel;

mem_: TMemo);

begin

pause:= Suspended_; inherited Create(Suspended_);

Interval := 10000000; // Интервал обращения к главному потоку (VCL) FCounter := 0; id := id_;

FCountTo := round(MAXINT/5); // Конечное число цикла

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

FreeOnTerminate := false; // Объект будет разрушаться программно

 

// для получения признака not Assigned()

setPriory(priory);

// Тип приоритета 0-6

progbar := progbar_; mesLabel := mesLabel_; mRep := mem_;

end;

Procedure TMyThread.starTHread; begin

StartTime := Time; if Suspended then

Start;

pause := false; end;

Procedure TMyThread.setPriory(set_: integer); begin

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

procedure TMyThread.Execute; begin

//Подготовка ProgressBar progbar.Max := FCountTo div Interval; progbar.Step := progbar.Max;

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 begin PctDone := (FCounter / FCountTo);

Synchronize(@DoWork); // Вывод в главном потоке VCL end;

Inc(FCounter);

end;

EndTime := Time;

//Завершить оформление вывода

if not(self.Terminated) then begin Dec(FCounter);

PctDone := (FCounter / FCountTo); Synchronize(@DoWork);

21

Приложение 2

end;

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

procedure TMyThread.DoWork; begin

progbar.Position := Round(progbar.Step * PctDone); mesLabel.Caption := FormatFloat('0.00 %', PctDone * 100);

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.

П2.2. Организация взаимодействия и управления (Lazarus)

Программное описание интерфейса управления потоком и синхронного взаимодействия потока с VCL. Ознакомиться с примером, создать собственный интерфейс управления потоками, в минимальном исполнении содержащий кнопки создания, запуска и останова-удаления потоков.

Модуль классов интерфейсов управления потоком. Интерфейсные элементы создаются из шаблона, хранимого в стандартном файловом потоке. Число интерфейсов создается по числу объявленных пользователем потоков. Интерфейсные панели размещаются в ячейках таблицы, выдают при выборе ID потока, а также управляют изменением приоритета потока. В свою очередь, потоки отображают на панелях прогресс выполнения задания через функцию синхронизации вывода. Данный упрощенный вариант вывода частично нарушает независимость потока при ожидании вывода, что является определенным недостатком выбранного подхода.

{Форма интерфейсных элементов потоков (процессов); методы взаимодействия потоков и интерфейсов; управление потоками через интерфейсы}

unit Unit1;

{$mode objfpc}{$H+} interface

uses

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

type

22

Приложение 2

{ TForm1 }

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

btStop: TButton; GroupBox5: TGroupBox;

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);

public

ListProcess: TList; // Список процессов

{ public declarations } end;

var

Form1: TForm1; threadlist:TCollthreadlist;

implementation {$R *.lfm}

{ TForm1 }

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

var

Stream: TFileStream ; St: TStringList;

S: String; begin

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

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

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

23

Приложение 2

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('Панель успешно сохранена в потоке'); 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:='Стоп';

end else

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

begin

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('Процессы выключены (не выгружены в память)!');

end;

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

procedure TForm1.btStopClick(Sender: TObject); begin

if not(Assigned(ListProcess)) then

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

if (ListProcess.Count=0) then

24

Приложение 2

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

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

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

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

//FreeAndNil(ListProcess); // Освобождаем ресурсы от процесса 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; 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 StringGrid1.Objects[0,i].Destroy;

StringGrid1.RowCount := 0; end;

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

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

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

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

Randomize; // Инициализировать генератор случайных чисел

25

Приложение 2

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;

//«Прямоугольник» в ячейке таблицы для размещения панели 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;

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

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

//Создать и добавить поток в коллекцию

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

end; // for

finally

26

Приложение 2

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.

П2.3. Многомерные потоки c управляемыми параметрами приоритета (CodeBlock C++)

Создание фиксированного числа потоков с управлением приоритетом. Основные особен-

ности примера: диагностика сообщений при изменении параметров потока; передача параметров в поток через объявленную структуру; назначение приоритета и псевдоимени потоку; синхронизация организация очереди вывода.

#include «pthread.h» #include «stdio.h» #include «iostream» #include «stdlib.h» #include «unistd.h» #include <errno.h>

using namespace std;

// Классификатор ошибки при изменении приоритета

27

Приложение 2

const char * errorcase(int _err) {

//EINVAL - у системы нет ресурсов для создания нового потока

//EPERM - вызывающий процесс не имеет прав суперпользователя

//ESRCH - thread является недействительным или уже прекращен

//EFAULT - неправильные атрибуты потока (переданные аргументом attr)

const char * nameerr; switch (_err) {

case (EINVAL): nameerr = «EINVAL»; break;

case (EPERM):

nameerr = «EPERM - вызывающий процесс не имеет прав суперпользователя»;

break;

case (ESRCH): nameerr = «ESRCH»; break;

case (EFAULT): nameerr = «EFAULT»; break;

default:

nameerr = «Ошибка не определена»;

}

return nameerr;

}

//Передача данных потоку структурой по ссылке void struct stridparam {

public:

stridparam (const char * _name,int _tPriority){ name = _name;

tPriority = _tPriority;

}

const char * name; // Псевдоимя (алиас)

int tPriority; // Значение устанавливаемого приоритета (0-99)

};

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

void assignRRPriority ( int tPriority, // Приоритиет pthread_t thread // Поток

) {

int policy; // Политика стратегии планирования потока struct sched_param param; // Параметр приоритета

// Текущие стратегия и приоритет pthread_getschedparam(thread, &policy, &param);

printf(«\n-------------------------------------------------------

»);

printf(«\nТекущее значение приоритета:»);

printf(«\nPriority: %d

currentPolicy: %d», param.__sched_priority, policy);

printf(« Priority min/max: %d/%d», sched_get_priority_min(policy), sched_get_priority_max(policy));

param.sched_priority = tPriority; // Устанавливаем приоритет

//Функция pthread_setschedparam() устанавливает как стратегию планирования,

//так и приоритет потока (0-99)

28

Приложение 2

//Атрибуты управления планированием потоков. Возможные значения —

//SCHED_OTHER=0 - стратегия планирования другого типа (по умолчанию),

//назначается новому потоку по умолчанию.

//Для процессов, выполняющихся с правами суперпользователя:

//SCHED_FIFO=1 - алгоритм «первым прибыл, первым обслужен»

//SCHED_RR=2 - круговая или циклическая схема планирования (по кванту времени)

int ret_ = pthread_setschedparam(thread, SCHED_RR, &param);

if (ret_) // корректный анализ ошибки

printf(«\nОшибка: \»%s\» установки приоритета %d \n», errorcase(ret_), tPriority);

else {

//Функция pthread_getschedparam() сохраняет значения

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

//в параметрах policy и param соответственно

pthread_getschedparam(thread, &policy, &param); printf(«\nИзмененное значение приоритета:»);

printf(«\nPriority: %d currentPolicy: %d», param.__sched_priority, policy);

printf(« Priority min/max: %d/%d», sched_get_priority_min(policy),

sched_get_priority_max(policy));

 

printf(«\n-------------------------------------------------------

\n»);

}

}

// Функция потока

void *myThreadFun (void *trparam) {

printf («\nНачало работы %s», static_cast <stridparam *> (trparam)->name); // Изменить приоритет

assignRRPriority(

//Приведение к типу struct ссылки типа void и доступ

//к переданному значению приоритета

static_cast <stridparam *> (trparam)->tPriority, pthread_self() // Текущий поток

);

for (int i=10; i>0; i--){ // Задание потоку - уменьшение счетчика sleep(1);

// Вывод псевдоимени и текущего значения счетчика

printf («\n %d - %s «, i, static_cast <stridparam *> (trparam)->name);

}

printf («\nЗавершение потока - %s \n»,static_cast <stridparam *> (trparam)->name);

return NULL;

}

int main () {

pthread_t thr0,thr1,thr2; // Объявление потоков отдельными переменными printf(«Потоки включены\n»);

// Создание потоков с передачей псевдоимени и приоритета

 

pthread_create(&thr0,NULL,myThreadFun, new stridparam(«

Vasja»,1));

pthread_create(&thr1,NULL,myThreadFun, new stridparam(«

Petja»,30));

pthread_create(&thr2,NULL,myThreadFun, new stridparam(«Superhero»,60)); // main ожидает завершения потоков

pthread_join(thr0,NULL); pthread_join(thr1,NULL); pthread_join(thr2,NULL);

29

Приложение 2

printf(«\nВыключаем потоки\n»); exit(0);

}

П2.4. Организация потоков с асинхронным выводом данных (Lazarus)

Программное описание классов формы и потока при асинхронном обмене данными.

Главный модуль проекта

program threadcollectIdle; {$mode objfpc}{$H+}

//Добавить для программ работы с потоками

{$DEFINE UNIX} {$DEFINE UseCThreads}

//=====

uses

{$IFDEF UNIX}{$IFDEF UseCThreads} cthreads,

{$ENDIF}{$ENDIF}

Interfaces, // this includes the LCL widgetset Forms, Unit1;

{$R *.res} begin

RequireDerivedFormResource := True; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run;

end.

Модуль классов потока и списка ссылок на поток. В основном цикле метода Execute потока исключена процедура Synchronize. Вызываемая процедура DoWork вместо вывода данных производит присвоение результатов расчетов внутренней переменной DataVision типа record, блокируя доступ к расчетам на время обновления данных. Исключается постановка потока в очередь на вывод визуальной информации.

{AСИНХРОННЫЙ ОБМЕН ДАННЫМИ С VCL; независимый процесс-поток; коллекция-лист объектов потока}

unit Unit2;

{$mode objfpc}{$H+}

interface

uses

Classes, SysUtils,Dialogs,STDCTRLS;

const

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

tpHigher,

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

tpLowest,

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

);

30

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