Шаг 283.
Модели потоков. Класс TThread

    На этом шаге мы рассмотрим особенности использования этого класса.

    В Delphi вычисления в потоках реализуются при помощи абстрактного класса TThread, для чего необходимо создать класс-потомок и при помощи директивы override перекрыть абстрактный метод Execute:

type
  TMyThread = class(TThread) 
  protected
    procedure Execute: override; 
  end;
  .   .   .
  procedure TMyThread.Execute; 
  begin
  // Программный код
  end;

    Код, который реализуется в методе Execute, выполняется в отдельном потоке. Для его запуска просто необходимо создать экземпляр класса TMyThread:

  ТМуThread.Create(False);

    СОМ-объекты самостоятельно, без использования класса TThread, поддерживают вычисления в потоках. Однако этот класс полезно рассмотреть с точки зрения анализа сложностей, возникающих при создании многопоточных приложений.

    При запуске любого приложения автоматически создается отдельный поток, который называют главным. В коде главного потока реализуется прием сообщений Windows, нрорисовка графики на экране, обработка событий (если обработчики специально не созданы так, чтобы выполняться в отдельном потоке). Создание экземпляра класса-потомка TThread означает, что код в методе Execute будет выполняться параллельно с кодом главного потока, периодически прерывая выполнение кода главного потока, - создается фоновый поток. Он может находиться в одном из двух состояний: ожидание и выполнение. Если фоновый поток находится в состоянии ожидания, то процессор ему просто не выделяет время. При этом все время отдается другим потокам. Соответственно, класс TThread имеет два метода:

    Конструктор класса TThread принимает в качестве параметра логическую переменную CreateSuspended. Если ее значение равно True, то конструктор полностью отрабатывается, но поток находится в состоянии ожидания. Для его запуска требуется вызов метода Resume. При значении этого параметра, равном False, код в методе Execute начинает выполняться немедленно после отработки конструктора. Нельзя вызывать конструктор класса-потомка TThread с параметром False, если происходит инициализация свойств в классе:

type
  TMyThread = class(TThread) 
  protected
    procedure Execute: override; 
  public
    FR: Single; 
  end;
  .   .   .   .
  procedure TMyThread.Execute; 
  var
    R: Single; 
  begin
  .   .   .   .
    R := Sqrt(FR - 1); 
  .   .   .   .
  end;
  .   .   .   .
  procedure TForm1.ButtonlClick(Sender:TObject); 
  begin
    with TMyThread.Create(False) do FR := 5; 
  end;

    На первый взгляд этот код не содержит ошибки: создается отдельный ноток, присваивается начальное значение переменной FR и продолжается расчет с использованием этого значения. Однако поскольку после отработки конструктора код, реализованный в методе Execute, выполняется в отдельном потоке, то оператор R := sqrt(FR-1) из метода ТМуThread.Execute может быть выполнен раньше оператора FR := 5 из метода TForm1.ButtonlClick. Соответственно, значение переменной FR после отработки конструктора равно 0, и в такой ситуации будет происходить исключение ЕInvalidOp (попытка извлечь квадратный корень из -1). Поиск и устранение такого типа ошибки усложняются тем, что, вообще говоря, эта ошибка непостоянна и в некоторых случаях может проявляться очень редко. Для устранения указанной ошибки код в методе Button1Click следует переписать:

  procedure TForm1.ButtonlClick(Sender:TObject); 
  begin
    with TMyThread.Create(True) do begin FR := 5; 
    Resume; 
    end; 
  end;

    В приведенном фрагменте кода первоначально создается экземпляр класса TMyThread в режиме ожидания, затем присваиваются начальные значения всем свойствам и переменным класса и только после этого вызывается метод Resume, который начинает выполнение кода. Это единственно правильный способ инициализации данных: поток не запускается до тех пор, пока не заданы значения всех переменных.

    На следующем шаге мы закончим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг