Форум программистов, компьютерный форум, киберфорум
Наши страницы
krapotkin
Войти
Регистрация
Восстановить пароль
Блог. Двадцать пять лет Делфи-практики

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

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

Начав с Делфи-2 двадцать пять лет назад, я прошел все версии, испробовал массу технологий, включая работу с БД, с графикой DirectX, связью с серверами и интернетом, разработку на Андроид и IOS, и многое, многое другое.
____________________________________________________________________________________
P.S. все, о чем здесь написано, всего лишь измышления из головы.
совпадения с реальными людьми и фактами случайны.
Рейтинг: 5.00. Голосов: 2.

ProcessMessages

Запись от krapotkin размещена 30.08.2017 в 06:54
Обновил(-а) krapotkin 30.08.2017 в 07:02

Как только я вижу в чьем-то коде Application.ProcessMessages, я сразу вспоминаю -
несчастные случаи на стройке

Поясню:
Вся программа на делфи выглядит примерно так
Delphi
1
2
3
Application.Initialize;
Application.CreateForm(Form1,TForm1);
Application.Run;
вот если посмотреть код в Application.Run, мы увидим (весьма примерно)
Delphi
1
2
while not Application.Terminated do
  Application.ProcessMessages;
итого. пока приложению никто не сказал закрыться, оно всего лишь смотрит в свою очередь сообщений и вызывает обработчики этих сообщений.
Сообщений сотни - каждое движение мыши, таймеры, перерисовка окантовки и заголовка окон, вообще все в Windows делается по сообщениям.
И вот при обработке сообщения программа вызывает ваш обработчик события. Например, нажатия кнопки.
А там вы делаете что-то очень важное для вас и хуже всего - длительное по времени.
И тут встает проблема. Перерисовка окна и обработка ввода идут тоже через сообщения, а программа еще не вышла из вашего обработчика и никаких сообщений не обрабатывает, и все говорят - висит!
Да нет, не висит, вы сами ее чем-то заняли, она работает. Чаще всего в к-нить вашем цикле, или качает что-то из интернета.
И для простого решения проблемы "зависания" программист пишет Application.ProcessMessages!
И вуаля - программа перерисовывает окно и даже вызывает еще какой-то обработчик мыши.
И тут начинаются спецэффекты
Напоминаю. Ваш обработчик вызван из какой-то процедуры компонента, которая по цепочке вызвана из обработчика события Application.ProcessMessages. И вы тоже вызываете ProcessMessages. Да еще в цикле!
Получится
ProcessMessages -- Обработчик сообщения -- Процедуры объектов -- Ваш обработчик -- ProcessMessages -- Обработчик сообщения -- Процедуры объектов -- ????
и вот тут запросто может быть заново вызван ваш обработчик опять!!! И дальше все по кругу...
Либо как вариант, компонент может полагаться на последовательные вызовы событий типа
OnCreate -- OnShow -- OnActivate
а тут еще Create не закончился, а уже Show. oO
Программа становится НЕЛИНЕЙНОЙ!
В нормальном состоянии ваши процедуры отрабатывают друг за другом, от начала и до конца. Никаких параллельностей.
Тут же это не так! Следующие процедуры запросто вызываются, когда еще не закончилась текущая. А потом текущая продолжится!

Конечно, не всегда это приводит к печальным последствиям, но делать так НЕ СТОИТ!
А если сделали, не удивляйтесь!

Иногда вместо ProcessMessages достаточно просто перерисовать компонент Component1.Update;
Иногда длительное действие нужно вынести в отдельный поток.
С потоками решение сложной задачи можно разбить на два этапа - старт задачи и, отдельно - получение результата, когда задача завершилась. Тогда программа может заниматься своими делами, когда расчет (или загрузка файла или...) будет закончен, вызовется обработчик, где вы решите, что делать с результатом.

Решения есть. Не ленитесь.
И перестаньте вставлять Application.ProcessMessages.

P.S.
Один из самых клевых результатов даёт нажатие на крестик Закрыть программу.
И программа уже думает, что всё - уничтожает формы и данные, а ваш обработчик продолжает обращаться к этим формам и данным, ессно получая AV. Никаких ошибок - просто так задумано вами - программистом.
Размещено в Без категории
Просмотров 1073 Комментарии 9
Всего комментариев 9
Комментарии
  1. Старый комментарий
    Аватар для Rius
    +100500
    Если в коде есть DoEvents (.Net Framework), processEvents (Qt Framework) или ProcessMessages (Delphi/C++ Builder), значит это говнокод и его надо переписать.
    Причём это не сами методы плохие, а их применение говорит о недостатке знания более правильных способов.
    Запись от Rius размещена 30.08.2017 в 07:14 Rius вне форума
    Обновил(-а) Rius 10.07.2018 в 06:42 (Исправлено название метода в Qt)
  2. Старый комментарий
    Аватар для Avazart
    Цитата:
    Если в коде есть
    Как правило да.
    Но теоретически можно написать код который будет работать нормально, другое дело что его написать довольно сложно при таком подходе и не оправдано.
    Запись от Avazart размещена 30.08.2017 в 12:58 Avazart вне форума
    Обновил(-а) Avazart 30.08.2017 в 13:01
  3. Старый комментарий
    Аватар для krapotkin
    согласен-1.
    согласен-2.
    и даже написал, что само по себе это не приведет к чему-то плохому
    иногда это даже оправданный выход.
    но тестирование дураком эта архитектура уж точно не пройдет)))
    Запись от krapotkin размещена 30.08.2017 в 13:33 krapotkin вне форума
  4. Старый комментарий
    Аватар для Jin X
    С отдельным потоком дров можно наломать ещё больше. Тут есть дополнительные нюансы типа необходимости использования критических секций (иногда), проблемы взаимодействия с VCL (хотя значения из компонентов нужно читать заранее, а не из цикла). Да и так же, как и при использовании ProcessMessages, нужно отключать кнопки повторного запуска события (и другие сопутствующие) + обрабатывать CloseQuery (нужно же обрывать цикл потока через глобальную переменную или event [TThread.Terminate опасно, если FreeOnTerminate = True] и потом ещё ждать завершения потока)

    Другое дело, что дополнительный поток не будет "отвлекаться" на ProcessMessages и может отработать быстрее + (если ProcessMessages вызывается не на каждой итерации, а раз в секунду, скажем) не будет мелких "провисов"...

    Но будет не менее прикольно, когда закрытие формы завершит поток фактически через TerminateThread (в момент ExitProcess). И форма ведь закроется (*), и не будет AV, что создаст иллюзию, что код написан верно. Ещё будет забавно новичку искать ошибку, если иногда вдруг (скажем, раз в 10 запусков) будут возникать AV при закрытии формы. Например, когда поток будет обращаться к уже освободившемуся объекту.

    Вывод такой: любым инструментом нужно уметь правильно пользоваться. А просто сказать новичку: юзай потоки вместо ProcessMessages тоже не вариант...

    p.s.
    (*) А вот при цикле с ProcessMessages форма не закроется, тут вы ошибаетесь, программа не даст ей закрыться при нажатии на крестик (или при вызове Close), если только вы не открыли её через Show (без "Modal"), но и в этом случае она не уничтожится, а просто уйдёт в Hide.
    Запись от Jin X размещена 09.07.2018 в 22:47 Jin X вне форума
    Обновил(-а) Jin X 09.07.2018 в 23:11
  5. Старый комментарий
    Аватар для Avazart
    Цитата:
    С отдельным потоком дров можно наломать ещё больше.
    Если мозгов нет, то можно ничего не делая наломать и не только дров.
    Запись от Avazart размещена 09.07.2018 в 23:01 Avazart вне форума
  6. Старый комментарий
    Аватар для Jin X
    Цитата:
    Если мозгов нет, то можно ничего не делая наломать и не только дров.
    А если есть, то и с ProcessMessages всё будет работать
    Я к тому, что нельзя говорить, что ProcessMessages – однозначно зло и говнокод, а потоки – благодать и признак мастерства. Для каждого случая свой инструмент, не так ли?
    Запись от Jin X размещена 09.07.2018 в 23:04 Jin X вне форума
    Обновил(-а) Jin X 09.07.2018 в 23:12
  7. Старый комментарий
    Аватар для Rius
    Цитата:
    А если есть, то и с ProcessMessages всё будет работать
    Новичкам это не грозит, т.к. никто из них не понимает очереди сообщений и непредусмотренного поведения от ProcessMessages.
    В справке Embarcadero даже ни слова нет о возможных проблемах, типа юзайте и будет вам счастье, вот же в простеньком "Hello World" всё наглядно показано. В Net Framework же предупреждают:
    Цитата:
    Calling this method causes the current thread to be suspended while all waiting window messages are processed. If a message causes an event to be triggered, then other areas of your application code may execute. This can cause your application to exhibit unexpected behaviors that are difficult to debug. If you perform operations or computations that take a long time, it is often preferable to perform those operations on a new thread.
    В сложной программе это источник адовых багов, которые замаешься отлавливать, что в общем и есть говнокод.
    Поэтому заранее надо учить правильным методам, а не костылям.
    Цитата:
    а потоки – благодать и признак мастерства
    Для новичков - признак движения к мастерству.
    Запись от Rius размещена 10.07.2018 в 05:58 Rius вне форума
    Обновил(-а) Rius 10.07.2018 в 06:26
  8. Старый комментарий
    Аватар для krapotkin
    Вот как раз новичку просто сказать не юзай ProcessMessages - это правильный вариант))
    Цитата:
    не уничтожится, а просто уйдёт
    гы. там что только не понаворочают как раз на обработчики. программа либо будет сыпать AV, и это проверено )) либо так и останется висеть полуубитая, потому что уже некому разбирать очередь сообщений ))
    Запись от krapotkin размещена 10.07.2018 в 06:21 krapotkin вне форума
  9. Старый комментарий
    Аватар для Avazart
    Цитата:
    А если есть, то и с ProcessMessages всё будет работать
    Не будет, а если и будет то через ... одним словом гавнокод

    Цитата:
    Я к тому, что нельзя говорить, что ProcessMessages – однозначно зло и говнокод, а потоки – благодать и признак мастерства. Для каждого случая свой инструмент, не так ли?
    Но именно так и есть.
    В данном случае ProcessMessages не инструмент.

    ProcessMessages в коде следствие непонимание этого факта и оправдание что бы и далее не изучать многопоточность.

    Думаю есть случаи когда ProcessMessages уместен, но этих случаев очень мало, и большая часть из них банальная лень писать код по нормальному.
    Запись от Avazart размещена 10.07.2018 в 23:03 Avazart вне форума
    Обновил(-а) Avazart 10.07.2018 в 23:08
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru