Сегодня нам предстоит познакомится с потоками. Эта штука
очень интересная и беспощадно нужная. Всё программирование звука будет написано
с использованием потоков, так что вникай где суть.
Представь, что ты хочешь вставить в свою прогу проверку
орфографии. Если ты после каждой нажатой клавиши будешь проверять правильность
слов, то твоя прога будет сумасшедше тормозить. А как же это делает MS Word ?
Очень просто, после запуска проги запускается поток, который в фоновом режиме
производит проверку орфографии. Ты спокойно набираешь текст и даже не ощущаешь
(почти) как параллельный процесс в свободное время производит сложнейшие
проверки твоих каракуль.
Любая программа содержит хотя бы один поток (главный), в
котором она выполняется. Помимо этого, она может порождать любое количество
дополнительных потоков, которые будут выполняться в фоновом режиме. У
дополнительных потоков приоритет выставляется такой же как и у главного потока
программы, но ты его можешь увеличить или уменьшить. Чем выше приоритет потока,
тем больше на него отводится процессорного времени.
Да что тут распинаться, давай программировать. Со всем
разберёмся в процессе.
Создай новый проект. Поставь на форму ТRichEdit из
палитры Win32 и один TLabel. Это мы создали форму, а теперь создадим поток.
Выбери File -> New (рисунок 1). Находишь там Thread
Object , выделяешь и щёлкаешь "ОК". Появляется окошко, как на рисунке 2. Вводим
имя потока (я ввёл TCountObj).
Сохраняем весь проект. Главную форму под именем main
(как всегда), а поток под именем MyThread.
После этого Delphi создаст вот такой код:
unit MyThread;
interface
uses
Classes;
type
TCountObj = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end;
implementation
{ Important: Methods and properties of objects in VCL can only be used in a
method called using Synchronize, for example,
Synchronize(UpdateCaption);
and UpdateCaption could look like,
procedure TCountObj.UpdateCaption;
begin
Form1.Caption := 'Updated in a thread';
end; }
{ TCountObj }
procedure TCountObj.Execute;
begin
{ Place thread code here }
end;
end.
Это новый поток. У объекта есть только одна функция
Execute . В этой функции мы и будем писать код потока. Напишем вот что:
procedure TCountObj.Execute;
begin
index:=1;
//Запускаем бесконечный счётчик
while index>0 do
begin
Synchronize(UpdateLabel);
Inc(index);
if index>100000 then
index:=0;
//Если поток остановлен, то выйти.
if terminated then exit;
end;
end;
index я объявил как integer в разделе private потока.
Там же я объявил процедуру UpdateLabel. Эта процедура выглядит так:
procedure TCountObj.UpdateLabel;
begin
Form1.Label1.Caption:=IntToStr(Index);
end;
И последнее, что я сделал - подключил главную форму в
раздел uses, потому что я обращаюсь к ней в коде выше (Form1.Label1.Caption).
Теперь о магической функции Synchronize. В качестве
параметра ей передаётся процедура UpdateLabel, которая производит вывод в
главную форму. Для чего нужно вставлять вывод на экран в Synchronize? Библиотека
VCL имеет один косяк - она не защищена от потоков. Если главная форма и поток
попробуют одновременно вывести что-нибудь в одну и ту же область экрана или
компонент, то программа рухнет как эфелева башня. Поэтому весь вывод на форму
нужно выделять в отдельную процедуру и вызывать эту процедуру с помощью
Synchronize.
Что происходит во время вызова Synchronize? В этот
момент поток останавливается и управление передаётся главному потоку, который и
произведёт обновление.
Наш поток готов. Возвращаемся к главной форме. Я её
сделал, как на рис 3. В раздел uses (самый первый, который идёт после interface)
я добавил свой поток MyThread.
В разделе private я объявил переменную co типа TCountObj
(объект моего потока).
По нажатию кнопки "Запустить" я написал такой код:
procedure TForm1.Button1Click(Sender: TObject);
begin
co:=TCountObj.Create(true);
co.Resume;
co.Priority:=tpLower;
end;
В первой строке я создаю поток co. В качестве параметра
может быть true или false. Если false, то поток сразу начинает выполнение, иначе
поток создаётся, но не запускается. Для запуска нужно использовать Resume, что я
делаю во второй строке.
В третьей строке я устанавливаю приоритет потока
поменьше, чтобы он не мешал работе основному потоку и выполнялся в фоне. Если
установить приоритет повыше, то основной поток начнёт притормаживать, потому что
у них будут одинаковые приоритеты.
По кнопке "Остановить" я написал:
procedure TForm1.Button1Click(Sender: TObject);
begin
co.Terminate;
co.Free;
end;
В первой строке я останавливаю выполнение потока, а во
второй уничтожаю его.
Попробуй запустить прогу, запустить поток (нажатием
кнопки "Запустить") и понабирать текст в RichEdit. Текст будет набиратся без
проблем, и в это время в ТLabel будет работать счётчик. Если бы ты запустил
счётчик без отдельного потока, то ты бы не смог набирать текст в RichEdit,
потому что все ресурсы программы (основного потока) уходили бы на работу
счётчика.
Итак, наш первый поток готов. При программировании звука
мы напишем более полезные примеры с потоками. А сейчас ещё несколько полезных
фишек:
- Suspend - приостанавливает поток. Для
вызова просто напиши co.Suspend. Чтобы возобновить работу с этой
же точки нужно вызвать Resume.
- Priority- устанавливает приоритет
потока. Например Priority:=tpIdle;
- tpIdle - поток будет
работать только когда процессор бездельничает.
- tpLowest - самый слабый
приоритет
- tpLower - слабый
приоритет
- tpNormal - нормальный
- tpHigher - высокий
- tpHighest - самый
высокий
- tpTimeCritical -
критичный (не советую использовать, потому что
может грохнуть систему).
- Suspended - если этот параметр true, то
поток находится в паузе.
- Terminated - если true, то поток должен
быть остановлен.
Вот и всё. С потоками окончено.
|