Вызов Synchronize
приводит к тому, что соответствующий метод будет выполнен основной нитью приложения, а нить, вызвавшая Synchronize
, будет приостановлена до тех пор, пока главная нить не сделает это. Отсюда видно, насколько бредовыми являются советы (заполонившие Интернет, а также встречающиеся в некоторых книгах, например, у Архангельского) помещать весь код нити в Synchronize
. В этом случае дополнительная нить вообще не будет ничего делать, все будет выполняться основной нитью, и выигрыша от создания дополнительной нити просто не будет. Поэтому в Synchronize
нужно помещать только те действия, которые не могут быть выполнены неосновной нитью (например, обращения к свойствам и методам VCL-компонентов).
Главная петля сообщений в VCL реализуется методом Application.Run
, вызов которого автоматически вставляется в dpr-файл VCL-проекта. Application.Run
вызывает в цикле метод HandleMessage
, пока поле FTerminate
не окажется равным True
(напомним, что значение True
присваивается этому полю, когда ProcessMessage
извлекает из очереди сообщение WM_QUIT
, а также при обработке сообщения WM_ENDSESSION
и при закрытии главной формы).
Для организации локальной петли сообщений существует метод Application.ProcessMessages
. Он вызывает ProcessMessage
до тех пор, пока очередь не окажется пустой. Вызов этого метода рекомендуется вставлять в обработчики событий, которые работают долго, чтобы в это время программа не теряла способности реагировать на действия пользователя.
Из сказанного может сложиться впечатление, что главная нить проверяет список методов синхронизации только в главной петле сообщений, когда вызывается метод Idle
. На самом деле это не так. Модуль Classes
содержит переменную WakeMainThread
, хранящую указатель на метод, который вызывается при помещении нового метода в список синхронизации. В конструкторе TApplication
этой переменной присваивается указатель на метод TApplication.WakeMainThread
, который посылает сообщение WM_NULL
невидимому окну приложения. Сообщение WM_NULL
— это "пустое" сообщение, на которое окно не должно реагировать (оно используется, например, при перехвате сообщений ловушкой: ловушка не может запретить передачу окну сообщения, но может изменить его на WM_NULL
, чтобы окно проигнорировало сообщение). Невидимое окно приложения, тем не менее, не игнорирует это сообщение, а вызывает при его получении CheckSynchronize
. Таким образом, синхронное выполнение метода не откладывается до вызова Idle
, а выполняется достаточно быстро, в том числе и в локальной петле сообщений. Более того, если главная нить перешла в режим ожидания получения сообщения (через вызов WaitMessage
), то вызов Synchronize
в другой нити прервет это ожидание, т.к. в очередь будет поставлено сообщение WM_NULL
.
Процедура CheckSynchronize
и переменная WakeMainThread
позволяют обеспечить синхронизацию и в тех приложениях, которые не используют VCL в полном объеме. Разработчику приложения необходимо обеспечить периодические вызовы функции CheckSynchronize
из главной нити, чтобы можно было вызывать TThread.Synchronize
в других нитях. При этом в главной нити можно обойтись без петли сообщений. Присвоение переменной WakeMainThread
собственного метода позволяет реализовать специфичный для данного приложения способ ускорения вызова метода в главной нити.