Пример создания многопоточного приложения в Delphi
- Код, вносимый в метод Execute, вычисляет число я, используя сходимость бесконечного ряда Лейбница:
Pi = 4-4/3 + 4/5-4/7 + 4/9 – …
Разумеется, отображать новое значение после каждой итерации – это то же самое, что стрелять из пушки по воробьям. На отображение информации система потратит в десятки раз больше времени, чем на собственно вычисления. Поэтому мы ввели константу updatePeriod, которая регулирует периодичность отображения текущего значения.
Код метода Execute показан ниже:
const // Лучше использовать нечетное число для того, чтобы избежать эффекта // мерцания UpdatePeriod = 1000001; procedure TPiThread.Execute; var sign: Integer; PiValue, PrevValue: Extended; i: Int64; begin { Place thread code here } PiValue: = 4; sign: = -1; i: = 0; repeat Inc(i); PrevValue: = PiValue; PiValue: = PiValue + sign * 4 / (2*i+l); sign: = -sign; if i mod UpdatePeriod = 0 then begin GlobalPi: = PiValue; GlobalCounter: = i; Synchronize(fmMain.UpdatePi); end; until Terminated or (Abs(PiValue – PrevValue)<1E-19); end;
- Откройте меню File и выберите пункт Save As. Сохраните модуль с потоком как uPiThread.pas.
- Отредактируйте главный файл модуля uMain.pas и добавьте модуль uPiThread к списку используемых модулей в секции интерфейса. Он должен выглядеть так:
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, StdCTRLs, uPiThread;
- В секции public формы TfmMain добавьте ссылку на создаваемую нить:
PiThread: TPiThread;
- Добавьте в модуль uMain две глобальные переменные
GlobalPi: Extended; GlobalCounter: Int64;
…и метод UpdatePi:
procedure TfmMain.UpdatePi; begin if Islconic(Application.Handle) then Exit; LaValue.Caption: = FloatToStrF(GlobalPi, ffFixed, 18, 18); lALTerNum.Caption: = IntToStr(GlobalCounter) + ' iterations'; end;
Этот метод, если вы обратили внимание, вызывается из потока посредством процедуры Synchronize. Он отображает текущее значение приближения к числу "пи" и количество итераций.
В случае, если главное окно приложения свернуто, отображение не производится; так что после его развертывания вам, возможно, придется подождать некоторое время для обновления.
- Выполните двойной щелчок на свободном месте рабочей области формы, при этом создастся шаблон метода FormCreate. Здесь мы отобразим значение системной константы р±:
procedure TfmMain.FormCreate(Sender: TObject); begin laBuiltln.Caption: = FloatToStrF(Pi, ffFixed, 18, 18); end;
- Выберите на форме переключатель (его название cbcalcuiate) и назначьте событию Onclick код, создающий и уничтожающий вычислительный поток в зависимости от состояния переключателя:
procedure TfmMain.cbCalculateClick(Sender: TObject); begin if cbCalculate.Checked then begin PiThread: = TPiThread.Create(True); PiThread.FreeOnTerminate: = True; PiThread.Priority: = tpLower; PiThread.Resume; end else begin if Assigned(PiThread) then PiThread.Terminate; end; end;
Таким образом, многопоточное приложение готово к запуску. Если все пройдет нормально, вы увидите картинку, подобную той, которая приведена на рис. 29.5.
Рис. 29.5. Выполняющееся приложение Threads1
Пока один из авторов писал текст этого раздела, запущенное одновременно приложение Threads1 выполнило пять миллиардов итераций и приблизилось к встроенному значению Pi в десятом разряде. Интересно, насколько хватит терпения у вас?
Этот простой пример – первый шаг в усвоении того, как от базового класса rrhread можно порождать собственные классы. Из-за своей простоты он не лишен недостатков; более того – если бы вычислительных нитей было не одна, а более, кое-какие приемы были бы даже ошибочными. Но – об этом ниже.