методички / 4045 ЭИ
.pdfПриложение 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, ¶m);
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, ¶m);
if (ret_) // корректный анализ ошибки
printf(«\nОшибка: \»%s\» установки приоритета %d \n», errorcase(ret_), tPriority);
else {
//Функция pthread_getschedparam() сохраняет значения
//стратегии планирования и приоритета
//в параметрах policy и param соответственно
pthread_getschedparam(thread, &policy, ¶m); 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