Delphi: потоки

КОМПЬЮТЕРНЫЕ КУРСЫ "ПОИСК"

[Главная страница] [Delphi] [Контакты]

Пример простого многопоточного приложения


Поместите на главную форму три компонента TProgressBar, присвоив всем трем вертикальную ориентацию Orientation =  pbVertical. Свойству Max, описывающему максимальное значение шкалы, назначим значение 1000. Под каждым компонентом TProgressBar поместите элемент управления TCheckBox (флажок). В правой части формы разместите три элемента TTrackBar (ползунок). У соответствующих компонентов TCheckBox и TTrackBar поменяйте значение свойства tag:

  • CheckBox1 и TrackBar1: Tag = 1
  • CheckBox2 и TrackBar2: Tag = 2
  • CheckBox3 и TrackBar3: Tag = 3

Последние три компонента проекта – метки TLabel – разместите над шкалами TProgressBar

potok

Последние три компонента проекта – метки TLabel – разместите над шкалами TProgressBar

При помощи элементов управления TTrackBar мы будем изменять приоритеты соответствующих потоков. Одновременно выберите все компоненты TTrackBar и их свойству Max присвойте значение 3. Значение 0 будет соответствовать минимальному приоритету tpIdle, значение 3 – нормальному приоритету tpNormal.

С настройкой элементов управления покончено. Займемся потоками. Для этого выберите пункт меню File → New → Other.

potok2

В окне New Items в разделе Delphi Files выберите пиктограмму Thread Object и нажмите ОК. Назовите класс создаваемого потока TMyThread. Вновь созданный модуль с шаблоном кода потока сохраните под именем ThreadUnit.pas.

potok3

В соответствии с приведенным ниже листингом внесите изменения в модуль ThreadUnit:

Листинг 1.


unit ThreadUnit;

interface

uses
  Classes, Windows, SysUtils, StdCtrls, ComCtrls;

type
  TMyThread = class(TThread)
  private
    { Private declarations }
    procedure UpdateControls;
  protected
    procedure Execute; override;
  public
    ProgressBar: TProgressBar; //поле потока для подключения к TProgressBar
    Lbl: TLabel; //поле потока для подключения к информационной метке
  end;

implementation

{ TMyThread }

procedure TMyThread.Execute;
var
  i: Integer;
begin
  while Terminated = False do
  begin
    i := 0;
    while i < 2000000 do Inc(i);  // единственная задача цикла –
                                  // занять процессорное время
    ReturnValue := ReturnValue + 1;
    Synchronize(UpdateControls);  // синхронизируемся с методами VCL
  end;
end;

procedure TMyThread.UpdateControls;
begin
  ProgressBar.Position := ProgressBar.Position + 1;
  if ProgressBar.Position >= ProgressBar.Max then
    ProgressBar.Position := 0;
  Lbl.Caption := IntToStr(ReturnValue);
end;

end.

В секции public потока опубликованы два поля ProgressBar : TProgressBar и Lbl : TLabel, предназначенные для организации взаимодействия потока с размещенными на главной форме шкалой и меткой. Основной метод Execute() выполняется до тех пор, пока он не будет разрушен вызовом функции Terminate. Внутри метода реализован цикл while I<2000000 do INC(I), предназначенный только для «пожирания» времени процессора. В заключение из тела метода Execute() вызывается функция Synchronize(UpdateControls), организующая обновление данных в принадлежащих потоку шкале и метке.

Вернемся к главной форме проекта и опишем ряд методов. В обработчике события OnCreate() формы поместите строки кода, создающие и запускающие все три потока.

Листинг 2.


var
  Thread1, Thread2, Thread3: TMyThread;
  
procedure TForm1.FormCreate(Sender: TObject);
begin
  Thread1 := TMyThread.Create(True);
  Thread1.Priority := tpIdle;
  Thread1.ProgressBar := ProgressBar1;
  Thread1.Lbl := Label1;

  Thread2 := TMyThread.Create(True);
  Thread2.Priority := tpIdle;
  Thread2.ProgressBar := ProgressBar2;
  Thread2.Lbl := Label2;

  Thread3 := TMyThread.Create(True);
  Thread3.Priority := tpIdle;
  Thread3.ProgressBar := ProgressBar3;
  Thread3.Lbl := Label3;

  Thread1.Resume;
  Thread2.Resume;
  Thread3.Resume;
end;

В момент создания потоков мы связываем их с элементами управления TProgressBar и TLabel. Обращаю внимание, что все три экземпляра потоков создаются приостановленными и стартуют практически вместе после того, как к ним будут применены все настройки.

Чтобы не забыть, сразу опишем событие закрытия формы, в которой последовательно разрушим все три потока:

Листинг 3.


procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Thread1.Terminate;
  Thread2.Terminate;
  Thread3.Terminate;
end;

Настал час элемента управления TTrackBar. Задача компонента устанавливать приоритет для соответствующего потока. Выберите любой из этих компонентов и следующим образом опишите его обработчик события OnChange():

Листинг 4.


procedure TForm1.TrackBar1Change(Sender: TObject);
var
  Priority: TThreadPriority;
begin
  {преобразуем позицию ползунка в значение приоритета}
  case (Sender as TTrackBar).Position of
    0 : Priority := tpIdle;   // фоновый
    1 : Priority := tpLowest; // выше фонового
    2 : Priority := tpLower;  // низкий
    3 : Priority := tpNormal; // нормальный
  end;

  {по tag элемента управления выясняем, какому из потоков
  требуется изменить приоритет}
  case (Sender as TTrackBar).Tag of
    1: Thread1.Priority := Priority;
    2: Thread2.Priority := Priority;
    3: Thread3.Priority := Priority;
  end;
end;

Назначьте описанный выше обработчик события OnChange() общим для всех компонентов TTrackBar.

Опишите обработчик события OnClick() любого из компонентов TCheckBox в соответствии с указанным ниже кодом и сделайте его общим для всех компонентов-флажков. Задача этой процедуры – приостановить/продолжить выполнение потока.

Листинг 5.


procedure TForm1.CheckBox1Click(Sender: TObject);
var
  Suspended: Boolean;
begin
  Suspended := (Sender as TCheckBox).Checked;
  case (Sender as TCheckBox).Tag of
    1: Thread1.Suspended := Suspended;
    2: Thread2.Suspended := Suspended;
    3: Thread3.Suspended := Suspended;
  end;
end;

potok4

Сделайте этот обработчик общим для всех элементов TCheckBox. Приложение готово.

Источник: Д.Осипов - Delphi. Профессиональное программирование.

Исходный код примера здесь. Выполнен на Delphi XE.