Потоки в Delphi выполняют функцию имитации псевдопараллельной работы приложения. Как известно, для организации многозадачности операционная система выделяет каждому приложению, выполняющемуся в настоящий момент, определённые кванты времени, длина и количество которых определяется его приоритетом. Поэтому объём работы, который приложение может выполнить, определяется тем, сколько таких квантов оно сможет получить в единицу времени. Для операционной системы каждый поток является самостоятельной задачей, которой выделяются кванты времени на общих основаниях. Поэтому приложение Delphi, умеющее создатьнесколько потоков, получит больше времени операционной системы, и соответственно сможет выполнить больший объём работы.
Создать дополнительный поток в Delphi поможет объект TThread. Ввести объект TThread в программу можно двумя способами:
1. с помощью Мастера; 2. вручную.
как указано выше. Также, поскольку класс потока описан в другом модуле, имя этого модуля необходимо добавить в секцию uses. Теперь можно запускать поток, даже если в его процедуре Execute нет ни единого оператора.
//Запускать поток будем нажатием на кнопку: procedure TForm1.Button1Click(Sender: TObject); begin //Вначале нужно создать экземпляр потока: MyThread:=TMyThread.Create(False); //Параметр False запускает поток сразу после создания, True - запуск впоследствии , методом Resume //Далее можно указать параметры потока, например приоритет: MyThread.Priority:=tpNormal; end;
end. Применение потоков Если в основной программе попробовать выполнить такой цикл:
while True do;
то приложение зависнет. А теперь поместите его в процедуру Execute. При нажатии на кнопку наш бесконечный цикл будет непрерывно выполняться в потоке, однако и приложение как целое не зависнет.
В предыдущем примере поток выполняет бесконечный цикл. Однако, поток также обладает возможностями, позволяющими из основной программы передать ему приказ прекратить работу. Метод потока Terminate устанавливает свойство Terminated потока в True. Анализируя это свойство, поток может понять, что он должен завершить работу. Пример:
procedure TMyThread.Execute; begin while True do if MyThread.Teminated then break; end;
Этот код выполняет бесконечный цикл. Однако, при выполнении в основной программе оператора
MyThread.Terminate;
цикл завершается, и поток прекращает свою работу.
При работе с потоками необходимо учитывать приоритет создаваемых потоков. Так, если в предыдущем примере запустить не один поток, а два или больше, нажав на кнопку несколько раз, то компьютер станет очень заметно "тормозить". Это происходит потому, что приоритет по умолчанию новых потоков - нормальный. Можно уменьшить его, задав
MyThread.Priority:=tpLower;
Этого достаточно, чтобы компьютер чувствовал себя более свободно. Вот таблица приоритетов:
Приоритет Описание tpIdleНизший приоритет. Поток получает время только тогда, когда операционая система находится в состоянии простоя. tpLowestПриоритет на два пункта ниже нормального tpLowerПриоритет на один пункт ниже нормального tpNormalНормальный приоритет tpHigherПриоритет на один пункт выше нормального tpHighestПриоритет на два пункта выше нормального tpTimeCriticalМаксимальный приоритет. Приоритет на уровне функций ядра операционной системы.
Вот вам готовый проект работы c потоками, для экспериментов.
В продолжение темы нужно рассмотреть Особенности взаимодействия потоков с объектами VCL Дело в том, что при работе с несколькими потоками в приложении необходимо гарантировать, что в данный момент только один из потоков может иметь доступ к свойствам и методам объекта VCL - визуального компонента Delphi. Для обеспечения этого условия в Delphi имеется специальный метод, в рамках которого и нужно вызывать процедуры, модифицирующие свойства и использующие методы визуальных компонентов.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
Динамическое создание компонентов в Delphi [Конец Урока]
Динамически создаваемые компоненты - это компоненты, место в памяти под которые выделяется по мере необходимости в процессе работы приложения. Этим они и отличаются от компонентов, которые помещаются на Форму при проектировании приложения. Возможность создавать компоненты динамически это очень большое удобство для программиста. Например, можно создавать в цикле сразу много однотипных компонентов, формируя из них массив, которым в дальнейшем очень просто управлять.
Все компоненты, как объекты, имеют множество свойств, определяющих их работу. При установке компонента на Форму из палитры большинство этих свойств определяются системой Delphi автоматически. При создании динамического компонента программист должен описать и настроить их вручную. Посмотрим, как это делается.
Прежде всего, для появления динамически создаваемого компонента нужно выделить под него место в памяти. Выделением места в памяти компьютера под любой компонент занимается конструктор типа объекта этого компонента - метод Create. Для этого сначала нужно описать переменную нужного типа, а затем для выделения памяти воспользоваться методом Create. Метод Create имеет параметр Owner, определяющий так называемого "владельца" для создаваемого компонента.
Хотя на самом деле владелец нужен не для создания, а для уничтожения компонента. То есть, при уничтожении компонента-владельца происходит автоматическое уничтожение всех компонентов, у которых он указан в качестве владельца. При обычной установке компонента из палитры система делает владельцем этого компонента Форму. Проще всего поступать так же. Однако можно указать в качестве владельца сам этот компонент, воспользовавшись в качестве параметра ключевым словом Self.
Далее. Когда компонент создан, то есть место в памяти под него выделено, можно задавать значения параметрам этого объекта. Прежде всего, это ещё один компонент, так называемый "родитель". Компонент-родитель будет отвечать за отрисовку нашего динамически создаваемого компонента. Это значит, что новый компонент появится в границах компонента-родителя.
Если компонент-владелец имеет тип TComponent, то есть может быть любым компонентом, то компонент-родитель уже имеет тип TWinControl. То есть это должен быть "оконный" компонент, умеющий принимать и обрабатывать сообщения от системы Windows. Это необходимо, так как компонент должен находиться в некоторой иерархии компонентов, принимающих и передающих сообщения от системы Windows. Нашему динамическому компоненту сообщения будут передаваться через компонент-родитель.
А некоторые компоненты вообще не умеют принимать сообщения от системы, и в процессе работы в этом случае ими также будет управлять компонент-родитель, например, Форма или Панель, на которой они находятся.
Естественно, компонент не может быть родителем для самого себя. Имя компонента-родителя просто присваивается свойству Parent создаваемого динамически компонента.
Вот общая схема "конструирования" динамически создаваемого компонента:
var Component: TComponent;//Описать переменную для компонента
begin Component:=TComponent.Create(Owner);//Задать владельца Component.Parent:=Parent;//Задать родителя end; На этом создание компонента можно считать законченным, и он успешно появляется (или "не появляется", если он не визуальный!) в приложении. Остальные свойства будут присвоены ему по умолчанию самой системой Delphi.
Естественно, для визуальных компонентов значения таких свойств по умолчанию как положение на Форме, ширина, высота "свежесозданного" динамически компонента мало кого устроят. Нужные размеры и положение компонента также придётся задать программисту.
Давайте для примера динамически создадим многострочный редактор, компонент Memo. Пусть он появляется на Форме по нажатию кнопки: procedure TForm1.Button1Click(Sender: TObject); var Memo: TMemo; begin Memo:=TMemo.Create(Form1); Memo.Parent:=Form1; Memo.Left:=50; Memo.Top:=50; Memo.Width:=250; Memo.Height:=100; Memo.Text:='Мама я родился!'; end;
Ребята, не забывайте присваивать (ассоциировать в Инспекторе Объектов) своим кнопкам предлагаемые здесь процедуры. А то потом в комментариях и появляются возгласы: "ничего не работает!"
Есть ещё свойство Name! По умолчанию Delphi присвоит ему типовое имя с присвоением очередного порядкового номера: Memo1. Программист при создании компонента также может присвоить свойству Name нужное значение, например:
Memo.Name:='DynamicallyCreatedMemo';
К данному компоненту можно обращаться как по этому имени, так и с указанием переменной, с помощью которой он был создан: Memo. Естественно, в последнем случае переменная должна быть глобальной.
Теперь хочу обратить ваше внимание на один нюанс. Создайте новый проект. Создайте-создайте, а то не получите того эффекта, на который я рассчитываю. В новом проекте создавать тот же компонент Memo мы будем в обработчике OnCreate Формы, чтобы Форма появлялась уже с нашим компонентом. Для пустой Формы создайте обработчик события OnCreate и наполните его вышеприведённым кодом. И что же - при попытке исполнить приложение мы получим от компилятора ошибку!
А дело вот в чём. Посмотрите в свежесозданном проекте, ещё до добавления компонентов, на список перечисленных в операторе uses стандартных модулей:
unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; Теперь возьмите в Палитре компонентов компонент Memo или ту же кнопку Button, положите на Форму и опять посмотрите на список модулей:
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; Подчёркиванием я выделил модуль, который автоматически добавил Delphi к списку необходимых модулей. Перед выполнением приложения компилятор просматривает установленные компоненты и добавляет в список uses модули, необходимые для работы этих компонентов. Так что даже если вы полностью сотрёте этот список ( оставьте только "uses Forms;"), Delphi его восстановит в том минимальном виде, который нужен для функционирования компонентов.
Поэтому естественно, что мы получили ошибку в предыдущем варианте - не хватало нужных модулей. Теперь смело жмите Run!
Также становится понятно, почему приложение, создающее Memo через нажате кнопки, сразу заработало. Для работы компонентов Button и Memo необходим один и тот же модуль StdCtrls, описывающий компоненты, расположенные на вкладке Standart. Поэтому, когда мы положили на Форму компонент Button, Delphi добавил этот модуль, что обеспечило работу также и Memo.
Так вот что нужно делать, чтобы подобная ошибка не появлялась! Просто положите на Форму из палитры компонентов компонент, который вы создаёте динамически, и жмите Run. Всё, после компиляции компонент можно удалять за ненадобностью - модуль, необходимый для его работы, Delphi затирать не станет! И ваш динамически создаваемый компонент будет работать.
Можно, конечно, обойтись и без добавления на Форму и последующего удаления компонентов, которые мы в программе будем создавать динамически. Посмотрите - при наведении мышки на любой компонент в Палитре Компонентов всплывает подсказка с названием модуля, в котором он описан. Добавьте это название в список uses, и всё! Но если ошибётесь в какой буковке - пеняйте тогда на себя.
Теперь возникает вопрос, как можно создать приложение аналогичное первому, где компонент Memo создавался динамически по нажатию кнопки, но чтобы кнопка тоже была создана динамически!
Динамически создать кнопку мы можем в обработчике OnCreate Формы, точно так же, как и компонент Memo. Однако она не будет работать - создавать компонент Memo. Естественно, ведь обработчика OnClick для неё мы не создали! Давайте сделаем это.
Естественно, проще всего создать обработчик для динамически создаваемого компонента, воспользовавшись готовым компонентом из палитры. Создаём для него нужный обработчик, а затем этот компонент просто удаляем. А теперь достаточно присвоить имя созданного обработчика соответствующему свойству динамического компонента. Название этого свойства будет совпадать с названием соответствующего события в Инспекторе Объектов. Например, для события OnClick пишем так:
Button.OnClick:=Button1Click;
Естественно, имя обработчика может быть любым, просто в данном случае я воспользовался тем обработчиком, что создал Delphi для кнопки из палитры. Если оно вам не нравится, можно смело переименовать процедуру, только не забудьте изменить её название также и в описании типа Формы.
Теперь мы умеем создавать компоненты динамически, и можем наделать компонентов столько, сколько нам требуется. Но такие уж они, динамически создаваемые компоненты, что надобность в них как появляется, так и исчезает - динамически! Иногда нужно и уничтожить компонент. Для уничтожения компонента есть простой метод: Free.
Component.Free;
Вот и всё. Все ресурсы, выделенные для функционирования компонента, будут освобождены. Останется только созданная переменная, указывающая на уже несуществующий в памяти объект. Её тоже неплохо бы уничтожить. Это делается присвоением переменной значения nil. Есть процедура, выполняющая оба эти действия, уничтожение и объекта и переменной: FreeAndNil:
FreeAndNil(Component);
И это относится не только к динамически созданному - к любому компоненту и объекту в памяти.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014