Я не понимаю, какое приложение.ProcessMessages в Delphi делает [закрыто]
Я noobie в Delphi, так что извините за возможные глупые вопросы.
мой руководитель объяснил мне это приложение.ProcessMessages предотвращает замораживание приложения и выделяет дополнительное вычислительное время. Но в документах этой команды всегда что-то объясняется о системе очередей, которая обрабатывается? Пожалуйста, кто-нибудь может объяснить мне контекст?
2 ответов
нет короткого способа ответить на этот вопрос правильно.
основным средством взаимодействия приложений Windows с операционной системой является система обмена сообщениями. Все, что происходит в приложении windows, происходит в ответ на сообщение.
например :
если вы нажмете на экран, операционная система решит, какое приложение было нажато, и опубликует сообщение этому приложению, указывающее, что оно получил щелчок (вместе с местоположением этого щелчка).
если окно перемещается и показывает часть вашего приложения под ним, операционная система отправляет сообщение о том, что ваше приложение перекрашивается.
список продолжается. Все, что происходит, управляется сообщениями.
теперь каждое приложение имеет один основной поток пользовательского интерфейса ("основной" поток), и этот поток имеет одну основную функцию - он работает в бесконечном цикле, который проверяет наличие этих сообщений в операционной системе, а затем выполняет необходимый код в ответ на эти сообщения.
Проблема
вы приходите и начинаете писать заявление. Вы можете написать такой код:
procedure TForm1.Button1Click(Sender: TObject);
var i : Integer;
begin
for i := 0 to 99999999999 do begin
SolveTheProblemsOfTheWorld(i);
CalculatePiToABillionPlaces;
end;
end;
в большей структуре программы выполнение в основном потоке выглядит следующим образом:
- проверить наличие сообщения
- для каждого сообщения-выполните связанное обработчики
- вернитесь, чтобы проверить наличие сообщений (цикл)
таким образом, этот цикл счастливо wizzing, когда внезапно один из связанных обработчиков (Button1Click
выше) начинает принимать очень длительное время. ключ к пониманию заключается в том, что один обработчик сообщений должен завершиться до запуска следующего. если вы, например, щелкните полосу прокрутки и перетащите ее, но вы прикрепили обработчик к OnClick
полосы прокрутки, которая занимает 10s для завершения, затем перетащите операция не будет видна вашим приложением, пока этот обработчик щелчка не завершится. Тем временем очередь сообщений заполняется, и основной поток ничего не делает с этим.
конечно, вы испытали это-вдруг ваше приложение не отвечает на клики. Вы не можете взаимодействовать с ним, вы не можете переместить окно, если вы перетащите другое окно вокруг него, приложение даже не перекрасится - оно просто заполняется тем мусором, который вы оставили поверх он.
Введите ProcessMessages
ленивое, ужасное решение не помещать ваш длительный код в поток
что вы делаете, когда вы звоните Application.ProcessMessages
, в середине одного из этих обработчиков, инструктируя основной поток сделать перерыв, чтобы вернуться, чтобы проверить очередь сообщений и очистить все сообщения, которые накапливались; обрабатывать любые щелчки, движения Окна, входы, нажатия клавиш, перекрасить себя если нужно и т. д.
procedure TForm1.Button1Click(Sender: TObject);
var i : Integer;
begin
for i := 0 to 99999999999 do begin
SolveTheProblemsOfTheWorld(i);
CalculatePiToABillionPlaces;
Application.ProcessMessages;
end;
end;
это может показаться поверхностно, как разумная вещь, чтобы сделать, так как это позволяет держать приложение отзывчивым во время длительного цикла. В конечном счете, однако, этот стиль программирования широко считается чрезвычайно плохой практикой по большому числу очень веских причин. Достаточно сказать, что везде вы испытываете соблазн использовать Application.ProcessMessages
является твердым случаем для перемещения этой работы в фоновый поток.
более подробно, давайте посмотрим на фактический код:
procedure TApplication.ProcessMessages;
var
Msg: TMsg;
begin
while ProcessMessage(Msg) do {loop};
end;
поэтому, когда вы делаете этот вызов Application.ProcessMessages
вы запускаете цикл, который один за другим очищает сообщения из очереди сообщений (и выполняет весь код, прикрепленный к обработчикам, которые реагируют на эти сообщения), пока он не будет пустым. Когда он пуст и больше нет сообщений для обработки, control вернется к следующей строке в вашей программе.
важный момент, котор нужно принять прочь что жидкость, ровная взаимодействие, которое вы испытываете при использовании программы, является полной иллюзией, которая зависит именно от этого цикла сообщений, обрабатываемого как можно быстрее. Чем меньше временная задержка между сообщением, отправляемым в ваше приложение, и обрабатываемым сообщением, тем больше ваше приложение будет чувствовать себя живым и отзывчивым.
именно по этой причине весь код, прикрепленный к обработчикам пользовательского интерфейса, должен работать быстро. Длительные операции должны быть прервано, чтобы обработка сообщений могла продолжаться (т. е.:Application.ProcessMessages
) или эти операции должны быть перемещены в отдельный поток, где они могут выполняться без привязки основного потока и снятия его с основной ответственности (которая заключается в сохранении пользовательского интерфейса).
для действительно хорошей статьи по этой теме см.:
Одиссея ключа Питер ниже
(ссылка на кэш Google... сервер кажется занятым для меня)
Аннотация: данная статья следует пути сообщения нажатия клавиши через VCL. Вы узнаете, как реализована обработка ключей, как работают события OnKey и какие точки вмешательства для программиста можно найти во всем процессе. Кроме того, объясняются такие вещи, как обработка сообщений, и вы узнаете, как отслеживать сообщения в отладчике из цикла сообщений в конечный пункт назначения.
сообщения
Delphi-это приложение для Windows, и много общения между компонентами Windows (например, формами, полями редактирования, но и скрытыми вещами, такими как таймеры) выполняется с помощью вещей, называемых сообщения.
эти сообщения отправляются в специальную очередь. Вы можете отправлять такие сообщения напрямую (используя SendMessage
и PostMessage
функции API), но сообщения также отправляются косвенно, когда вы устанавливаете свойство, например Text
в TEdit
. Эти сообщения используются для уведомления элементов управления, которые значимы событий произошли в рамках программы, поэтому они могут реагировать соответствующим образом. Эта концепция отправки сообщений и ответа на них является основным принципом парадигмы, известной как программирование на основе событий.
многие современные операционные системы, управляемые событиями, такими как Windows. На самом деле Delphi VCL обертывает много функций Windows, и многие вещи, которые вы делаете, приведут к отправке сообщений между элементы управления, которые уведомляют эти элементы управления щелчками мыши, нажатиями клавиатуры, изменением настроек Windows, закрытием приложения, перемещением элемента управления и т.д.
Mainthread не обрабатывает сообщения при выполнении кода
операционные системы также позволяют программам, чтобы иметь вещи, называемые потоками. Потоки похожи на небольшие части программы, которые работают одновременно (и в некоторых случаях работают одновременно). Приложение может иметь один или несколько потоков, но всегда есть основной поток, который также отвечает за прием входящих сообщений и обновление GUI.
когда основной поток приложения простаивает (не выполняет код), он проверяет очередь, чтобы увидеть, есть ли сообщения и обработать их. В то время как другой несвязанный код выполняется, this не может произойдет. Рассмотрим следующий пример:
Label1.Caption := 'A';
Sleep(5000); // Sleep is simulating a long process.
Label1.Caption := 'B';
если вы выполняете этот код, вы ожидаете, что метка получит заголовок "A", который меняется на " Б " через пять секунд. Но это не так. Установка заголовка метки вызывает перекраску элемента управления с помощью сообщения. Поскольку основной поток по-прежнему заблокирован этим кодом (даже командой Sleep), сообщение еще не обработано.
только когда прошло 5 секунд и заголовок установлен, основной поток становится бездействующим и выполнит перерисовки, которые мы вызвали, установив заголовок. Также обратите внимание, что другое взаимодействие, например, нажатие кнопки или перетаскивание окна откладывается до истечения 5 секунд. Весь основной поток замерзает.
приложение.ProcessMessages на помощь
приложение.ProcessMessages просто заставит приложение очистить свою очередь сообщений, поэтому вы можете "исправить" код следующим образом:
Label1.Caption := 'A';
Application.ProcessMessages;
Sleep(5000);
Label1.Caption := 'B';
обычно в вашем коде не будет просто сна, но много фактической обработки. Используя Application.ProcessMessage
часто, вы можете сохранить интерфейс приложение отвечает, даже когда вы выполняете код.
но это немного грязно, основной поток (и интерфейс) все равно будет заблокирован, если вам не удастся сжать много звонков Application.ProcessMessages
. И помимо обработки сообщений paint, обрабатываются и другие сообщения, поэтому приложение может обрабатывать событие click прямо в середине вашего процесса.
Итак, документы, которые Вы читаете, правы:Application.ProcessMessages
будет пустой сообщение очередь. Ваш начальник не совсем прав. Он не выделяет дополнительное время на обработку, но он просто интегрирует опорожнение очереди сообщений в выполняемый код, в то время как обычно сообщения остаются в очереди до тех пор, пока приложение не станет простаивать.
внутреннее устройство
внутренне, само приложение делает то же самое все время. Его основная процедура,Application.Run
, выглядит так (упрощенный псевдо код):
repeat
ProcessMessageFromTheQueue;
If QueueIsEmpty then
TellWindowsToWakeMeUpWhenANewMessageArrives;
until Terminated;
это обработка, которая выполняет код, например, если WM_MOUSECLICK
сообщение обработано, оно вызовет (через некоторую магию Delphi VCL), ваш Button1Click
обработчик событий. Поскольку это один поток, все работает последовательно, поэтому вы поймете, что ProcessMessageFromTheQueue
возвращается только после завершения обработчика событий. Delphi обрабатывает только одно сообщение за раз, и только делает следующее, когда обработка предыдущего завершена.
или это?
Application.ProcessMessages
выглядит так:
repeat
ProcessMessageFromTheQueue;
until QueueIsEmpty;
таким образом, он делает почти то же самое, что и основной цикл приложения: он выбирает сообщение из очереди и обрабатывает его. Таким образом, это означает, что вы можете обрабатывать следующие сообщения, пока вы находитесь внутри обработки последнего. Удобный трюк, но он может легко сделать ваше приложение грязным.
правильный путь: потоки
большинство людей считают, что это лучшее решение для использования отдельный поток для выполнения кода и просто отправлять сигналы в основной поток, когда ему нужно обновить метку или индикатор выполнения. Таким образом, основной поток все время простаивает и будет продолжать отвечать на щелчки мыши и т. д.
Threading трудно для новичка, хотя. Некоторые вещи, чтобы иметь в виду:
- поток (кроме основного потока) не должен напрямую обновлять GUI (например, поток не может установить Label1.Подпись. Это может неудача.
- переменные и другие ресурсы не должны изменяться несколькими потоками одновременно, поэтому вы должны принять меры предосторожности, чтобы предотвратить это (критические разделы).
- вы должны (в большинстве случаев) предотвратить выполнение пользователем того же действия, которое уже выполняется.
- во многих случаях вы должны найти правильный способ, чтобы прервать выполнение потока, если пользователь хочет отменить процесс или закрыть приложение.
в любом случае, этот ответ не должен быть полный курс о резьбе, но теперь, по крайней мере, вы знаете, что Application.ProcessMessages
для.
Также обратите внимание, что это считается "злом" многими людьми, но как новичок вы можете попробовать его в любом случае, чтобы узнать его сильные и слабые стороны самостоятельно. Если да, то я надеюсь, что эта информация, по крайней мере, вдохновит вас перейти к теме, Как только вы наберетесь опыта для следующей главы.