Глава 4. Простое разрушение потока.

Содержание:

Проблемы завершения, остановки и разрушения потоков.

В Главе 2 было упомянуто несколько проблем, возникающих при завершении потока, главные из них:

Эти темы тесно взаимосвязаны. Если потоку не нужно передавать никакую информацию в основной поток VCL, когда он завершен, или если используются методы, описанные в предыдущей части (передача результатов прямо перед остановкой потока), тогда и нет необходимости основному потоку VCL заниматься очисткой порожденного потока. В этих случаях можно установить для потока FreeOnTerminate в True и позволить потоку выполнить освобождение собственной памяти самому. Помните только, что в этом случае пользователь может выйти из программы в любой момент, в результате чего все потоки завершатся, и вероятны непредвиденные последствия. Если поток только записывает что-то в память или связывается с другими частями приложения, то это не проблема. Если же он пишет данные в файл или работает с разделяемыми системными ресурсами, то такой метод неприемлем.

Если поток должен обмениваться информацией с VCL после завершения, то следует обеспечить механизм синхронизации главного потока VCL с рабочим потоком, и основному потоку VCL придется выполнить очистку (вы должны написать код освобождения потока). Два механизма для этого будут описаны ниже.

Следует помнить еще об одной вещи:

Это происходит довольно часто. Некоторые потоки, особенно те, что занимаются вводом-выводом, содержат в себе выполнение бесконечного цикла: программа всегда может получить новые данные, а поток должен быть готов к их приему и обработке до тех пор, пока программа работает.

Итак, рассмотрим эти вопросы в обратном порядке...

Досрочная остановка потока.

В некоторых ситуациях одному потоку может потребоваться уведомить другой поток о своем завершении. Это обычно происходит, когда поток выполняет длительную операцию, и пользователь решает выйти из приложения, или операция должна быть прервана. TThread обеспечивает простой механизм для поддержки таких действий, а именно, метод Terminate и свойство Terminated. Когда поток создается, свойство Terminated установлено в False, а всякий раз, когда вызывается метод Terminate, свойство Terminated для этого потока устанавливается в True. Таким образом, на всех потоках лежит ответственность за периодическую проверку, не были ли они остановлены, и если это случается, за корректное завершение своей работы. Заметьте, что никакой крупномасштабной синхронизации при этом не происходит: когда один поток устанавливает свойство Terminated другого, нельзя предполагать, что другой поток тут же прочитает значение своего свойства Terminated и начнет процесс завершения. Свойство Terminated является просто флагом, говорящим "пожалуйста, завершайся как можно скорее". Диаграмма иллюстрирует эту ситуацию.

При проектировании объектов потока стоит уделить внимание проверке свойства Terminated, если это может потребоваться. Если же ваш поток блокирован в результате действия любого из механизмов синхронизации, обсуждаемых ниже, вы можете перекрыть метод Terminate для его разблокировки. Не забудьте, что нужно вызывать унаследованный метод Terminate перед разблокированием вашего потока, если хотите, чтобы следующая проверка Terminated возвратила True. Вот пример -  небольшая модификация потока расчета простых чисел из предыдущей части с добавлением проверки свойства Terminated. Я полагаю, что допустимо, если поток вернет неверный результат при установленном свойстве Terminated.

Событие OnTerminate.

Событие OnTerminate происходит, когда поток в самом деле завершается. Оно не случается, когда вызывается метод потока Terminate. Это событие может быть весьма полезным, поскольку оно выполняется в контексте основного потока VCL, подобно методам, вызываемым с помощью Synchronize. Таким образом, если есть желание выполнять какие-то действия VCL с потоком, который автоматически освобождается по окончании, то обработчик этого события - прекрасное место для таких действий. Для большинства программистов, начинающих работать с потоками, это наиболее удобный путь получения данных из не-VCL потока без особых усилий, не требующий явных вызовов синхронизации.

Как можно видеть на диаграмме, OnTerminate работает в основном так же, как и Synchronize, и семантически это почти идентично вызову Synchronize в конце потока. Основная польза от такой ситуации заключается в том, что используя флаг, например, "AppCanQuit" или счетчик работающих потоков в главном потоке VCL, можно обеспечить простые механизмы проверки того, что основной поток VCL завершается только тогда, когда все другие потоки остановлены. Существуют некоторые тонкости синхронизации, особенно если программист должен помещать вызов Application.Terminate в событие OnTerminate потока, но эти проблемы будут рассмотрены позже.

Контролируемая остановка потока - Подход 1.

В данном примере мы используем код для простых чисел из Главы 3 и модифицируем его так, чтобы пользователь не мог неумышленно закрыть приложение, когда выполняется рабочий поток. Это довольно просто. Фактически нам и не нужно модифицировать код потока. Мы просто добавим поле счетчика ссылок к основной форме, увеличивая его при создании потоков, создадим обработчик события OnTerminate, который будет уменьшать счетчик ссылок, и когда пользователь попытается закрыть программу, мы покажем, если потребуется, диалоговое окно предупреждения.

Пример показывает как несложно этого достичь: весь код, относящийся к отслеживанию числа работающих потоков, исполняется в основном потоке VCL, и этот код управляется событиями, как обычно и делается в Delphi-приложениях. В следующей части мы рассмотрим более сложный подход, который имеет некоторые преимущества при использовании продвинутых механизмов синхронизации.


[Содержание] [Назад][Вперед]

© Martin Harvey 2000.
 

Hosted by uCoz