| 
 Особенности разработки потоков
 
 
Современные операционные системы Windows 32 обеспечивают не только многозадачность, т. е. возможность параллельной работы нескольких программ, но и многопоточность, когда в рамках одной программы организуется несколько параллельно выполняемых фрагментов (потоков), каждый из которых конкурирует с другими потоками за наиболее важный ресурс - время центрального процессора. В многопоточном режиме время ЦП выделяется для каждого процесса небольшими порциями (квантами), по истечении этого времени управление передается другому потоку и т. д. до тех пор, пока потоки не закончат свою работу. В любой работающей программе организуется как минимум один поток для команд программы. С помощью объектов класса TThread программа может создать дополнительные потоки для проведения некоторой фоновой работы (например, текстовый процессор Word создает дополнительные потоки для проверки правильности орфографии, разбивки на страницы, печати документа и т. п.). 
 
Для создания дополнительного потока в программах Delphi предназначен специальный модуль потока в репозитории он обозначен пиктограммой Thread Obiecll). При выборе этого модуля Delphi запрашивает имя класса, который будет дочерним для основополагающего класса TThread. Необходимость наследования связана с тем, что класс TThread содержит абстрактный метод Execute, который, собственно, и должен исполняться в рамках нового потока и который, следовательно, обязан перекрываться в потомках. 
 
После указания имени дочернего класса Delphi раскрывает дополнительный модуль с обширным комментарием и заготовкой для дочернего класса. 
 
Например (с соответствующим переводом): 
 
unit Unit1;
interface
uses
  Classes;
type
  MyThread = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  end;
implementation
{ Важно: Методы и свойства объектов из библиотеки визуальных компонентов
могут использоваться только в рамках вызова метода Synchronize, например:
Synchronize(UpdateCaption);
где метод UpdateCaption должен быть подобен такому
procedure MyThread.UpdateCaption;
begin
  Formi.Caption := 'Новый текст метки';
end; }
(MyThread}
procedure MyThread.Execute;
begin
  { Пожалуйста, поместите код потока в этом месте }
end;
end.
Программирование потока ничем не отличается от программирования обычной программы за одним важным исключением: поток не должен использовать методы и свойства визуальных компонен тов, которые приводят к изменению внешнего вида программа Точнее, он может это делать только при обращении к специальному методу synchronize, с помощью которого осуществляется синхронизация исполнения главного потока программы с дополнительным потоком. 
 
Для иллюстрации приемов работы с потоком создадим программу, которая будет непрерывно обновлять содержимое многострочного редактора и при этом осуществлять математические вычисления. 
 
Для ее создания сначала на пустую форму поместите панель TPpanel, очистите ее свойство caption и поместите в Align значение аlRight - эта панель предназначена для размещения редактора TSpinEdit, кнопки TButton и индикатора TGauge и всегда должна располагаться в правой части окна программы. Поместите на панель перечисленные компоненты так, как это показано на рисунке (компоненты TSpinEdit и TGuage находятся на странице samples палитры компонентов).  
 
Установите в свойство SpinEditl.Value 3начение 2, присвойте свойству Gaugel. Kind значение gkPie, Gaugel. BorderStyle-bsNone и Button1.Caption — 'Квадрат'. 
 
На свободное место формы положите компонент TMemo и установите для него в свойство Align значение alСlient, а свойство Name- 'mmOutput'. 
 
Теперь создадим обработчик события Button1.Click: при нажатии на кнопку вначале содержимое редактора SpinEdit1 возводится в квадрат до тех пор, пока отображаемое в нем значение не слишком большим (больше 10 +1233 ). В этот момент надпись на кнопке меняется на “корень”, а нажатие на нее вычисляет корень квадратный ИЗ величины SpinEdit1. 
 
Дважды щелкните по кнопке Button1и напишите такой код: 
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  if Tag = 0 then
  begin
    SpinEditl.Text := Float - ToStr(sqr(StrToPloat(SpinEditl.Text)));
    if StrToFloat(SpinEditl.Text) > 1 el233 then
    begin
      Tag := 1;
      Buttoni.Caption := 'Корень'
    end
  end
  else
  begin
    SpinEditl.Text := FloatToStr(sqrt(StrToFloat(SpinEditl.Text)));
    if StrToFloat(SpinEditl.Text) < 2 then
    begin
      SpinEditl.Value := 2;
      Tag := 0;
      Button1.Caption := 'Квадрат'
    end
  end
end;
Таким образом, главный код программы связан с извлечением корня или возведением в квадрат величины, записанной в редакторе SpinEditl. 
 
Теперь создадим модуль потока, в методе Execute которого будем непрерывно формировать по 100 строк в редакторе mmOutput и показывать процент заполнения редактора с помощью индикатора Gaugel. 
 
Выберите пиктограмму модуля потока в окне репозитория Delphi и дайте наследнику класса Thread имя ThreadDemo. Окончательный текст модуля потока представлен ниже. 
 
unit Unit2;
interface
uses
  Classes;
type
  ThreadDemo = class(TThread)
  private
    { Private declarations }
  protected
    S: string;
    N: Integer;
    procedure UpdateMemo;
    procedure UpdateGauge;
    procedure Execute; override;
  end;
var
  TDemo: ThreadDemo;
implementation
uses Uniti, SysUtils;
{ Important: Methods and properties of objects in VCL can only re used.
in a method called using Synchronize, for example,
Synchronize(UpdateCaption);
and UpdateCaption could look like,
procedure ThreadDemo.UpdateCaption;
begin
  Formi.Caption := 'Updated in a thread';
end;
ThreadDemo}
procedure ThreadDemo.Execute;
var
  j, k: Integer;
begin
  repeat
    S := '';
    Synchronize(UpdateMemo);
    for k := 0 to 99 do
    begin N := k;
      S: = ' ';
      for j := 1 to 20 do
        S := S + FormatFloat('00', k), Synchronize(UpdateMemo);
      Synchronize(UpdateGauge)
    end;
  until False
end;
procedure ThreadDemo.UpdateMemo;
begin
  with .Form1.mmOutput.Lines do
    if S = ' ' then
      Clear else
      Add(S)
end;
procedure ThreadDemo.UpdateGauge;
begin
  Form1.Gaugel.Progress := N
end;
end.
Если вы запустите таким способом подготовленную программу, то ничего не произойдет - ведь мы еще не запустили поток. Чтобы сделать это, добавьте в модуле Unit1 главной формы ссылку uses Unit1, раскройте в окне Инспектора объектов список компонентов, выберите компонент Form1 и на его странице Event дважды щелкните по свойству onActivate, чтобы создать такой обработчик этого события:
 
procedure TForm1.FormActivate(Sender: TObject);
begin
  TDemo := ThreadDemo.Create(False),
end; 
Вот так просто запускается дополнительный поток - мы инициируем объект TDemo, передавая в его Консруктор ThreadDemo.Create 
 
единственный параметр False (этот параметр показывает, должен ли вновь созданный поток “спать” - True или он обязан немедленно начать работу - False). Программа в любой момент может приостановить работу потока, присвоив его свойству suspended значение True, и продолжить его выполнение, присвоив этому свойству значение False. Обратите внимание - метод Execute потока вынесен в секцию protected и поэтому недоступен из основного модуля. Выполнение этого метода начинается автоматически, как только свойство suspended примет значение False. 
 
Для обращения к свойствам и методам визуальных компонентов формы Form1 предназначен специальный метод потока Synchronize. Единственным параметром обращения к этому методу должно быть имя любой потоковой процедуры без параметров. Внутри такой процедуры разрешается обращаться к методам и свойствам визуальных компонентов. В нашем потоке две такие процедуры - UрdateMemo и updateGuage. В первой строка s добавляется к содержимому редактора mmoutput, во втором - глобальная переменная n присваивается свойству progress индикатора Gauge1. Поскольку эти процедуры не могут иметь параметров, для управления их работой приходится использовать глобальные переменные S и N. 
 |