Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Освоение среды операционной системы Microsoft W...doc
Скачиваний:
57
Добавлен:
09.11.2019
Размер:
6.83 Mб
Скачать

5.2.4. Синхронизация порожденного потока с родительским

При программировании метода Execute невозможно просто обратиться к свойствам и методам форм, а также их компонентов, поскольку эти действия приводят к конфликту между потоками. Например, нельзя изменить свойство Caption формы, потому что в этот момент главная программа может его читать. Проблема состоит в том, что, разделяя время работы процессора, ОС может в любой момент прервать поток и пере­дать управление другому потоку. Если поток не успеет скопировать все сим­волы в свойство Caption за отведенный ему квант времени и будет прерван, то главный поток программы прочтет из этого свойства некорректное значение. Описанная ситуация относится к числу самых безболезненных, на самом деле последствия одновременного доступа к общим объектам оказываются непред­сказуемыми и могут привести даже к аварийному завершению работы приложения.

Чтобы в потоках работать с формами и компонентами, нужно синхронизировать работу потока с главной программой. В классе потока необходимо объявить метод, который выполняет установку свойства Caption формы:

void __fastcall TMyThread::SetFormCaption()

{

Form1->Caption = "Searching..." ;

}

Чтобы объект Form1 был видим из модуля MyThrd, необходимо подключить в модуль MyThrd заголовочный файл Main.h (используя директиву # include).

В программном коде потока (в методе Execute) следует вызвать специальный метод Synchronize и передать ему ссылку на свой метод:

void __fastcall TMyThread::Execute()

{

Synchronize(SetFormCaption) ;

}

Метод Synchronize дождется подходящего момента времени и выполнит указан­ный вами метод атомарно по отношению к главному потоку приложения. Такое решение исключает все возможные конфликты.

5.2.5. Управление приоритетом обслуживания потока

По истечении каждого кванта ОС выполняет пла­нирование потоков, принимая решение о том, какому из них передать управ­ление. Планирование потоков выполняется в соответствии со шкалой приори­тетов. Очередной квант времени процессора отдается потоку с наивысшим приоритетом. Если несколько потоков имеют одинаковый приоритет, то управ­ление передается тому, который ожидает своей очереди дольше всех.

Приоритет потока представлен в классе TThread свойством Priority. Приоритет потока можно повышать и понижать по мере необходимости. Возможные зна­чения свойства перечислены в таблице 9.

Таблица 9

Значение

Описание

tpIdle

tpLowest

tpLower

tpNormal

tpHigher

tpHighest

tpTimeCritical

Поток выполняется, только если система находится в режиме ожидания

Приоритет потока на два пункта ниже обычного

Приоритет потока на один пункт ниже обычного

Поток имеет обычный приоритет

Приоритет потока на один пункт выше обычного

Приоритет потока на два пункта выше обычного

Поток имеет наивысший приоритет

5.2.6. Приостановка и продолжение работы потока

Выполнение потока можно временно приостановить извне, а затем продол­жить с помощью методов Suspend и Resume. Их вызовы могут быть вложенны­ми. Прежде, чем процесс действительно продолжит работу, необходимо столько раз вызвать метод Resume, сколько раз до этого вызывался метод Suspend. Если необходимо проверить состояние потока (приостановлен или работает), исполь­зуется свойство Suspended. Это свойство доступно по записи и может использо­ваться как альтернатива методам Suspend и Resume.

Прекращение работы потока. Для прекращения работы потока извне используется метод Terminate. Он уста­навливает свойство Terminated в значение true, сигнализируя о том, что поток должен завершиться. Метод Execute должен периодически проверять свойство Terminated: если оно равно true, необходимо немедленно завершить выполнение метода. Завершение метода Execute прекращает работу потока и генерирует событие OnTerminate в главном потоке приложения. Событие OnTerminate позволяет приложению отреагировать на завершение своего потока и обычно обра­батывается формой. После завершения потока объект потока автоматически разрушается, если его свойство FreeOnTerminate установлено в значение true. Если свойство FreeOnTerminate равно false (устанавливается по умолчанию), раз­рушение объекта потока возложено на программиста.

Переменные, дублируемые для каждого нового потока. Все глобальные переменные, объявленные обычным образом в языке C++, разделяются всеми потоками приложения. Если же нужно получить гло­бальные переменные, дублируемые для каждого нового потока, следует использовать зарезервированное слово __thread, например:

int __thread a;

double __thread b, с;

В результате данного объявления для каждого нового потока будут создавать­ся копии переменных а, b, с. В результате каждый поток сможет использовать их в своих целях без всякой синхронизации с другими потоками.

Многопоточность в приложениях с БД. Использование многопоточности дает хороший результат при работе с БД SQL-типа. Если запрос к БД выполняется достаточно долго, то для этого лучше создать отдельный поток, предоставив пользователю воз­можность продолжить работу с другими данными.

Каждый поток, обращающийся к БД, должен иметь свой собственный компонент Session (а значит, собственные компоненты Query, Table и, если не­обходимо, Database). Не визуальный компонент Session как раз и существует для того, чтобы можно было создавать потоки, работающие с таблицами парал­лельно во времени. Кроме того, при работе с общими компонен­тами, объектами и функциями модуля данных должна быть обеспечена синхро­низация потока с другими потоками, в том числе с главным потоком програм­мы. Для этих целей служит метод Synchronize объекта TThread.

5.2.7. Пример реализации потока в С++ Builder 4

Приведем хорошо известный пример порядка действий программиста при создании многопоточного приложения, когда в одном потоке (главном) должен воспроизводиться видео-файл при помощи приложения MediaPlayer, в другом – происходить полет шарика [9].

  1. Создаем новое приложение (New Application).

  2. В ставляем приложение MediaPlayer. Оно находится во вкладке System (рис.46).

Рис.46

  1. Вставляем компоненту Panel. Она будет являться дисплеем для MediaPlayer. Вставляем компоненту Timage для отображения действия с шариком. Подключаем пять кнопок Остановки воспроизведения, Воспроизведения, Открытия файла воспроизведения, Остановки параллельного потока и его запуска. Получим Диалог открытия файлов для открытия воспроизводимых файлов.

Расположение всех элементов на рабочем поле имеет вид (рис.47).

Р ис.47

  1. О беспечим работу MediaPlayer и воспроизведение файлов. В окне «Object Inspector» (рис.48) в свойстве MediaPlayer Display нужно выставить Panel1. Тогда MediaPlayer будет отображать видео файлы на Panel1. Кроме того, следует установить на dtAutoSelect свойство Device Type для автоматического определения типов воспроизводимых файлов.

Рис.48

Чтобы по нажатию клавиши MediaPlayer воспроизводил файл, необходимо описать функцию для нажатия клавиши:

void __fastcall TForm1::MPStrBtnClick(TObject *Sender)

{

MediaPlayer1->Open();

MediaPlayer1->Play();

}

Функция Open() открывает файл, который хранится в свойстве MediaPlayer->FileName.

Функция Play() воспроизводит открытый Файл.

Так же опишем функцию для клавиши, по которой MediaPlayer остановит воспроизведение.

void __fastcall TForm1::MPStpBtnClick(TObject *Sender)

{

MediaPlayer1->Stop();

}

Аналогично описывается функция для открытия файла.

void __fastcall TForm1::MPOpnBtnClick(TObject *Sender)

{

if (OpenDialog1->Execute())

{

MediaPlayer1->FileName=OpenDialog1->FileName;

}

MediaPlayer1->Open();

}

Теперь MediaPlayer будет воспроизводить необходимые файлы, причем это будет производиться в главном потоке.

  1. Обеспечим, чтобы в параллельном процессе обрабатывалось передвижение шарика. Для этого необходимо создать класс потока. В приведенной программе это класс Potok2.

Описания методов Synhr1 и Synhr2, а также метода Execute() даны в Приложении 3.

При анализе приведенного исходного кода стоит обратить внимание на следующие моменты. Конструктор объекта TMyThread создает приостановленный поток, вызывая унаследованный конструктор с параметром true (этот па­раметр называется Suspended). В результате поток создается, но работу еще не начинает. Это необходимо для исключения конфликтов между потоками при последующей инициализации полей объек­та. Для запуска потока главная программа должна будет не только создать объект потока, но и установить его свойство Suspended в значение false.

Чтобы при завершении потока объект автоматически разрушался, конструктор устанавливает свойство FreeOnTerminate в значение true.

Для рисования шарика метод Execute использует вложен­ную рекурсивную функцию Shar. В теле этой функции выполня­ются периодические проверки свойства Terminated; если оно равно true, происходит немедленный выход из функции, возврат из ре­курсии и завершение метода Execute.

Отображение Шарика требует обращения к совме­стно используемым объектам VCL. Для исключения конфликтов между потоками эти действия оформлены как методы Synhr1 и Synhr2, вызываемые с помощью метода Synchronize.