Синхронизации потоков

Если вы создали шаблон класса автоматически, то, наверное, заметили комментарий, какой Delphi поместил в новый модуль. Он говорит: "Methods and properties of objects in visual components can only be used in a method called using Synchronize". Это значит, что обращение к визуальным компонентам возможно только путем вызова процедуры Synchronize. Давайте рассмотрим пример, но теперь наш поток не будет разогревать процессор зря, а будет делать что-либо полезное, например, прокручивать ProgressBar на форме. Как параметр в процедуру Synchronize передается метод нашего потока, но сам он передается без параметров. Параметры можно передать, прибавив поля нужного типа в описание нашего класса. У нас будет одно поле - тот же прогресс :

 

TNewThread = class (TThread)

private

Progress: integer;

procedure SetProgress;

protected

procedure Execute; override;

end;

...

 

procedure TNewThread.Execute;

var

i: integer;

begin

for i: = 0 to 100 do

begin

sleep (50);

Progress: = i;

Synchronize (SetProgress);

end;

end;

 

procedure TNewThread.SetProgress;

begin

Form1.ProgressBar1.Position: = Progress;

end;

Вот теперь ProgressBar двигается, и это полностью безопасно. А безопасно вот почему: процедура Synchronize на время приостанавливает выполнение нашего потока, и передает управление главного потока, то есть SetProgress выполняется в главном потоке. Это надо запомнить, потому что некоторые допускают ошибки, выполняя внутри Synchronize длительную работу, при этом, что очевидно, форма зависает на длительное время. Поэтому используйте Synchronize для выведения информации - тот же двигатель прогресса, обновления заглавий компонентов и тому подобное

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

 

const

PROGRESS_POS = WM_USER 1;

 

В объявление класса формы прибавим новый метод, а потом и его реализацию:

 

TForm1 = class (TForm)

Button1: TButton;

ProgressBar1: TProgressBar;

procedure Button1Click (Sender: TObject);

private

procedure SetProgressPos (var Msg : TMessage); message PROGRESS_POS;

public

(Public declarations)

end;

...

 

procedure TForm1.SetProgressPos (var Msg : TMessage);

begin

ProgressBar1.Position: = Msg.LParam;

end;

 

Теперь мы немного изменим, можно сказать даже упростим, реализацию метода Execute нашего потока :

procedure TNewThread.Execute;

var

i: integer;

begin

for i: = 0 to 100 do

begin

sleep (50);

SendMessage (Form1.Handle, PROGRESS_POS, 0, i);

end;

end;

 

Используя функцию SendMessage, мы посылаем окну программы сообщения, один из параметров которого содержит нужный нам прогресс. Сообщение становится в очередь, и в соответствии с этой очередью будет обработано главным потоком, где и выполнится метод SetProgressPos. Но здесь есть один нюанс: SendMessage, как и в случае с Synchronize, приостановит выполнение нашего потока, пока основной поток не обработает сообщения. Если использовать PostMessage этого не случится, наш поток отправит сообщение и продолжит свою работу, а уже когда оно там будет проработано - неважно. Какую из этих функций использовать - решать вам, все зависит от задания.

Вот, в принципе, мы и рассмотрели основные способы работы с компонентами VCL из потоков. А как быть, если в нашей программе не один новый поток, а несколько? И нужно организовать работу с одними и теми же данными? Здесь нам на помощь приходят другие способы синхронизации. Один из них мы и рассмотрим. Для его реализации нужно прибавить в проект модуль SyncObjs.

 

Критические секции

Работают они таким способом: внутри критической секции может работать только один поток, другие ожидают его завершения. Чтобы лучше понять, везде приводят сравнения с узкой трубой: представьте, с одной стороны "толпятся" потоки, но в трубу может "пролізти" только один, а когда он "пролізе" - начнет движение второй, и так по порядку. Еще проще понять это на примере и тем же ProgressBar 'ом. Следовательно, запустите один из примеров, приведенных раньше. Нажмите на кнопку, подождите несколько секунд, а потом нажмите еще раз. Что происходит? ProgressBar начал прыгать. Прыгает потому, что у нас работает не один поток, а два, и каждый из них передает разные значения прогрессу. Теперь немного переделаем код, в событии onCreate формы создадим критическую секцию:

var

Form1: TForm1;

CriticalSection: TCriticalSection;

...

procedure TForm1.FormCreate (Sender: TObject);

begin

CriticalSection: = TCriticalSection.Create;

end;

 

У TCriticalSection есть две нужны нам методу, Enter и Leave, соответственно вход и выход из нее. Поместим наш код в критическую секцию:

 

procedure TNewThread.Execute;

var

i: integer;

begin

CriticalSection.Enter;

for i: = 0 to 100 do

begin

sleep (50);

SendMessage (Form1.Handle, PROGRESS_POS, 0, i);

end;

CriticalSection.Leave;

end;

Попробуйте запустить программу и нажать несколько раз на кнопку, а потом посчитайте, сколько раз пройдет прогресс. Понятно, в чем суть? Первый раз, нажимая на кнопку, мы создаем поток, он занимает критическую секцию и начинает работу. Нажимаем второй - создается второй поток, но критическая секция занята, и он ожидает, пока ее не освободит первый. Третий, четвертый - все пройдут только по-черзі.

Критические секции удобно использовать при обработке одних и тех же данных (списков, массивов) разными потоками.

 








Дата добавления: 2016-02-27; просмотров: 434;


Поиск по сайту:

При помощи поиска вы сможете найти нужную вам информацию.

Поделитесь с друзьями:

Если вам перенёс пользу информационный материал, или помог в учебе – поделитесь этим сайтом с друзьями и знакомыми.
helpiks.org - Хелпикс.Орг - 2014-2024 год. Материал сайта представляется для ознакомительного и учебного использования. | Поддержка
Генерация страницы за: 0.011 сек.