14
Потоки в Delphi Потоки в Delphi выполняют функцию имитации псевдопараллельной работы приложения. Как известно, для организации многозадачности операционная система выделяет каждому приложению, выполняющемуся в настоящий момент, определённые кванты времени, длина и количество которых определяется его приоритетом. Поэтому объём работы, который приложение может выполнить, определяется тем, сколько таких квантов оно сможет получить в единицу времени. Для операционной системы каждый поток является самостоятельной задачей, которой выделяются кванты времени на общих основаниях. Поэтому приложение Delphi,

Потоки в Delphi

Embed Size (px)

DESCRIPTION

Потоки в Delphi. - PowerPoint PPT Presentation

Citation preview

Page 1: Потоки в  Delphi

Потоки в Delphi

Потоки в Delphi выполняют функцию имитации псевдопараллельной работы приложения. Как известно, для организации многозадачности операционная система выделяет каждому приложению, выполняющемуся в настоящий момент, определённые кванты времени, длина и количество которых определяется его приоритетом. Поэтому объём работы, который приложение может выполнить, определяется тем, сколько таких квантов оно сможет получить в единицу времени. Для операционной системы каждый поток является самостоятельной задачей, которой выделяются кванты времени на общих основаниях. Поэтому приложение Delphi, умеющее создать несколько потоков, получит больше времени операционной системы, и соответственно сможет выполнить больший объём работы.

Page 2: Потоки в  Delphi

Потоки в Delphi

Создать дополнительный поток в Delphi поможет объект TThread. Ввести объект TThread в программу можно двумя способами: с помощью Мастера;вручную.   1. Мастер создания дополнительного потока в Delphi создаёт отдельный модуль, в рамках которого выполняется поток. Выполним:

   File -> New -> Other...

В появившейся табличке выбора найдём TThread Object. Появится окошко, в верхнюю строку которого (Class Name) введём имя нашего будущего потока: MyThread. В результате будет создан модуль, содержащий заготовку кода, реализующего дополнительный поток Delphi

Page 3: Потоки в  Delphi

Потоки в Delphi

unit Unit2; // Имя модуля, содержащего поток. При сохранении его можно изменить.Interfaceuses  Classes;type  MyThread = class(TThread) //MyThread - имя потока.  private    { Private declarations }  protected    procedure Execute; override;  end;implementation{ Important: Methods and properties of objects in visual components can only be  used in a method called using Synchronize, for xample,      Synchronize(UpdateCaption);  and UpdateCaption could look like,    procedure MyThread.UpdateCaption;    begin      Form1.Caption := 'Updated in a thread';    end; }{ MyThread }procedure MyThread.Execute;begin  { Place thread code here }end;

end.

Page 4: Потоки в  Delphi

Потоки в Delphi

В первом способе класс MyThread был создан мастером в дополнительном модуле. Второй способ состоит в том, что мы сами создаём такой класс в рамках одного из уже существующих модулей программы, например, в модуле Unit1:

unit Unit1; //Обычный модуль в котором описывается основная программаinterfaceuses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;type  TForm1 = class(TForm)    Button1: TButton;    procedure Button1Click(Sender: TObject);  private    { Private declarations }  public    { Public declarations }  end;//Здесь необходимо описать класс TMyThread:  TMyThread = class(TThread)    private    { Private declarations }  protected    procedure Execute; override;  end;

Page 5: Потоки в  Delphi

Потоки в Delphi

var  Form1: TForm1;//Нужно ввести переменную класса TMyThread  MyThread: TMyThread;

implementation

{$R *.dfm}

//Нужно создать процедуру Execute, уже описанную в классе TMyThreadprocedure TMyThread.Execute;begin//Здесь описывается код, который будет выполняться в потокеend;

Page 6: Потоки в  Delphi

Потоки в Delphi

Если поток создаётся мастером, т.е. в другом модуле, то не забудьте в основном модуле описать переменную - экземпляр потока. Также, поскольку класс потока описан в другом модуле, имя этого модуля необходимо добавить в секцию uses. Теперь можно запускать поток, даже если в его процедуре Execute нет ни единого оператора.

//Запускать поток будем нажатием на кнопку:procedure TForm1.Button1Click(Sender: TObject);begin//Вначале нужно создать экземпляр потока:  MyThread:=TMyThread.Create(False);//Параметр False запускает поток сразу после создания, True - запуск впоследствии , методом Resume//Далее можно указать параметры потока, например приоритет:  MyThread.Priority:=tpNormal;end;

end.

Page 7: Потоки в  Delphi

Потоки в Delphi

Применение потоков

Если в основной программе попробовать выполнить такой цикл:

   while True do;

то приложение зависнет. А теперь поместите его в процедуру Execute. При нажатии на кнопку наш бесконечный цикл будет непрерывно выполняться в потоке, однако и приложение как целое не зависнет.

procedure TMyThread.Execute;begin   while True do;end;

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

Page 8: Потоки в  Delphi

Потоки в Delphi

Метод потока Terminate устанавливает свойство Terminated потока в True. Анализируя это свойство, поток может понять, что он должен завершить работу. Пример:

procedure TMyThread.Execute;begin  while True do    if MyThread.Teminated then break;end;

   Этот код выполняет бесконечный цикл. Однако, при выполнении в основной программе оператора

  MyThread.Terminate;

цикл завершается, и поток прекращает свою работу.

Page 9: Потоки в  Delphi

Потоки в Delphi

При работе с потоками необходимо учитывать приоритет создаваемых потоков. Так, если в предыдущем примере запустить не один поток, а два или больше, нажав на кнопку несколько раз, то компьютер станет очень заметно "тормозить". Это происходит потому, что приоритет по умолчанию новых потоков - нормальный. Можно уменьшить его, задав

MyThread.Priority:=tpLower;

Этого достаточно, чтобы компьютер чувствовал себя более свободно.• tpIdle Низший приоритет. Поток получает время только

тогда, когда операционая система находится в состоянии простоя.

• tpLowest Приоритет на два пункта ниже нормального • tpLower Приоритет на один пункт ниже нормального • tpNormal Нормальный приоритет • tpHigher Приоритет на один пункт выше нормального • tpHighest Приоритет на два пункта выше нормального • tpTimeCritical Максимальный приоритет. Приоритет на

уровне функций ядра операционной системы.

Page 10: Потоки в  Delphi

Потоки в Delphi

При использовании в приложении нескольких потоков необходимо гарантировать, что в данный момент только один из потоков может иметь доступ к свойствам и методам объекта VCL - визуального компонента Delphi, то есть действия потоков необходимо синхронизировать между собой. Для выполнения такой синхронизации в Delphi применяется специальный метод Synchronize, в рамках которого и нужно вызывать процедуры, модифицирующие свойства визуальных компонентов.Процедура Synchronize использует в качестве параметра те процедуры, в которых происходит модификация свойств визуальных компонентов, и блокирует одновременный доступ к компоненту нескольких потоков. Вот какой пример, в частности, содержится в модуле, сгенерированном Мастером создания потока:

{Важно: Методы и свойства объектов в визуальных компонентах могут вызыватьсятолько в методе Synchronize, например:}procedure MyThread.UpdateCaption;begin  Form1.Caption := 'Updated in a thread';end;

procedure MyThread.Execute;begin  Synchronize(UpdateCaption);end;

Page 11: Потоки в  Delphi

Потоки в Delphi

В данном случае поток используется для изменения заголовка Формы. Изменение заголовка происходит в процедуре UpdateCaption. Казалось бы, для изменения заголовка эту процедуру достаточно вызвать в основной процедуре потока, Execute. Однако, если несколько таких потоков в программе одновременно попытаются изменить заголовок Формы, то это может привести к непредсказуемым последствиям. Для исключения этого процедура UpdateCaption вызывается в процедуре Execute как параметр метода Synchronize.

  Нужно знать, что метод Synchronize выполняется в главном потоке приложения. Поэтому, работая с несколькими потоками в приложении и применяя метод Synchronize, нужно учитывать, что: во-первых, частый вызов Synchronize тормозит выполнение приложения;во-вторых, если практически все процедуры выполняющегося потока выполняются с использованием метода Synchronize, то смысла в создании такого потока нет - всё равно его работа пройдёт в главном потоке.

Page 12: Потоки в  Delphi

Потоки в Delphi

Как пример рассмотрим всё ту же модификацию заголовка Формы. Пусть в одном из потоков происходит работа с большим массивом, и требуется отображать какой объём массива уже обработан. Для этого организуем поток, который будет выполнять эту работу. Будем выводить в заголовок Формы индекс элемента, с которым обрабатывающий поток работает в данный момент. Делать это будем с периодичностью 10 раз в секунду. Сначала сделаем так:

procedure TMyThread.UpdateCaption;beginwhile True do  begin    Form1.Caption:=IntToStr(I);//I - глобальная переменная основной

//программы, индекс массива    sleep(100);  end;end;

procedure TMyThread.Execute;begin  Synchronize(UpdateCaption);end;

Page 13: Потоки в  Delphi

Потоки в Delphi

Видим, что происходит именно то, о чём написано выше. Так как весь код потока, и модификация заголовка Формы, и цикл ожидания, выполняется в методе Synchronize, а значит в главном потоке, то приложение будет выглядеть зависшим, и его даже будет невозможно корректно завершить.Теперь попробуем вывести цикл за пределы Synchronize:

procedure TMyThread.UpdateCaption;begin  Form1.Caption:=IntToStr(Cap);end;

procedure TMyThread.Execute;beginwhile True do  begin    Synchronize(UpdateCaption);    sleep(100);  end;end;

Это правильный вариант. С помощью метода Synchronize выполняется только непосредственная модификация Заголовка Формы, а цикл ожидания выполняется в потоке, и не мешает главному потоку.

Page 14: Потоки в  Delphi

Потоки в Delphi

Некоторым объектам VCL процедура Synchronize не требуется, так как они всё же умеют корректно работать с потоками, либо нуждаются в других методах синхронизации.

Так, корректно работают с потоками • компоненты доступа к базам данных (с использованием

компонентов класса TSession). Исключение составляют базы данных Microsoft Access;

• классы, которые работают непосредственно с графикой. Это TFont, TPen, TBrush, TBitmap, TMetafile и TIcon. Канву объектов этих классов (Canvas) можно использовать не применяя метод Synchronize. Это делается с помощью блокировки канвы. То есть, поток, использующий в данный момент канву, предварительно блокирует канву методом Lock, препятствующим другим потокам работать с канвой, и разблокирует затем методом UnLock.