Delphi. Потоки по книге Мартина Харви. Конспект

Запуск потока

Примеры анонимных запусков

Традиционный запуск

Полный модуль традиционного запуска из Main

С объявлением класса потока, например так…


Флаг Terminated для потока

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

В коде потока мы пишем что-то такое…


Событие OnTerminate

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

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


Остановка основного потока после остановки дополнительных потоков. Пример


Syncronize из рабочего потока и WaitFor из основного потенциально может привести к DeadLock

Зацикливание может произойти для двух
потоков, если вычислительный поток вызывает Synchronize прямо перед
тем,как основной поток вызывает WaitFor. Тогда вычислительный поток
будет ждать, пока основной поток не вернется в цикл обработки
сообщений, а основной будет ждать завершения вычислительного.
Произойдет зацикливание. Возможно также, что основной поток VCL
вызовет WaitFor незадолго до вызова Synchronize рабочим потоком. Это
тоже может привести к зацикливанию. К счастью, разработчики VCL
предусмотрели перехват такой ошибки: в рабочем потоке возбуждается
исключение, таким образом цикл прерывается, и поток завершается.

Наилучший метод не допускать этой формы зацикливания – не
использовать WaitFor и Synchronize в одном приложении. От WaitFor можно
избавиться, применяя событие OnTerminate либо WaitFor + PostMessage.

Ниже пример получения результатов потока через WaitFor и PostMessage…


Ограничения метода Synchronize

У метода Synchronize есть несколько недостатков, благодаря которым он
подходит лишь для простых многопоточных приложений.
– Synchronize полезен лишь при взаимодействии между рабочим
потоком и основным потоком VCL.
– Использование Synchronize подразумевает,что рабочий поток ждет,
пока основной поток VCL будет в состоянии ожидания, даже когда
это не так уж и необходимо.
– Если приложение часто использует Synchronize, главный поток VCL
становится “узким местом”, и возникают проблемы с
производительностью.

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


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

Простой пример – пишем что-то в буфер TStringList, и каждый поток обращается к разделяемому ресурсу через FCS. Enter / FCS.Leave

Код потока

Код Main

Результат работы

Видно, что потоки получали доступ к буферу и Caption по очереди. Сначала первый, потом второй, потом первый, второй и так далее…

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

Функции ожидания критической секций ждут бесконечно. Нет возможности указать ждать столько-то секунд или отказаться от выполнения.

To be continued…

This entry was posted in Без рубрики. Bookmark the permalink.