Подтвердить что ты не робот

Как использовать DoEvents(), не будучи "злым"?

Простой поиск DoEvents приводит к большому количеству результатов, которые приводят, в основном, к:

DoEvents - зло. Не используйте его. Вместо этого используйте threading.

Причины, как правило, приводятся ниже:

  • Проблемы с возвратом
  • Плохая производительность
  • Проблемы с удобством использования (например, перетаскивание по отключенному окну)

Но некоторые известные функции Win32, такие как TrackPopupMenu и DoDragDrop, выполняют собственную обработку сообщений, чтобы поддерживать пользовательский интерфейс, как это делает DoEvents.
И все же, ни один из них, похоже, не сталкивается с этими проблемами (производительность, повторное включение и т.д.).

Как они это делают? Как избежать проблем, связанных с DoEvents? (Или они?)

4b9b3361

Ответ 1

DoEvents() опасно. Но держу пари, ты каждый день делаешь много опасных вещей. Буквально вчера я запустил несколько взрывных устройств (будущие читатели: обратите внимание на оригинальную дату публикации относительно определенного американского праздника). С осторожностью мы иногда можем объяснить опасности. Конечно, это означает, что вы знаете и понимаете, в чем опасность:

  • Проблемы повторного входа. Здесь на самом деле есть две опасности:

    1. Часть проблемы здесь связана со стеком вызовов. Если вы вызываете .DoEvents() в цикле, который сам обрабатывает сообщения, использующие DoEvents() и т.д., Вы получаете довольно глубокий стек вызовов. Легко использовать DoEvents() и случайно заполнить ваш стек вызовов, что приводит к исключению Qaru. Если вы используете .DoEvents() только в одном или двух местах, вы, вероятно, в порядке. Если это первый инструмент, к которому вы обращаетесь, когда у вас есть длительный процесс, вы можете легко оказаться здесь в беде. Даже одно использование в неправильном месте может позволить пользователю вызвать исключение (иногда просто удерживая клавишу ввода), и это может быть проблемой безопасности.
    2. Иногда можно найти один и тот же метод в стеке вызовов дважды. Если вы не создали метод с учетом этого (подсказка: вы, вероятно, не сделали), тогда могут произойти плохие вещи. Если все, что передано методу, является типом значения, и нет никакой зависимости от вещей вне метода, вы можете быть в порядке. Но в противном случае вам нужно тщательно подумать о том, что произойдет, если весь ваш метод будет запущен снова, прежде чем вам вернется управление в точке, где вызывается .DoEvents(). Какие параметры или ресурсы вне вашего метода могут быть изменены, чего вы не ожидали? Меняет ли ваш метод какие-либо объекты, где оба экземпляра в стеке могут действовать на один и тот же объект?
  • Проблемы с производительностью. DoEvents() может создать иллюзию многопоточности, но это не реальное многопоточность. В этом есть как минимум три настоящие опасности:

    1. Когда вы вызываете DoEvents(), вы передаете управление существующим потоком обратно в насос сообщений. Насос сообщений может, в свою очередь, передать управление чему-то другому, и это может занять некоторое время. В результате ваша оригинальная операция может закончиться гораздо дольше, чем если бы она находилась в потоке сама по себе, который никогда не возвращает управление, определенно дольше, чем нужно.
    2. Дублирование работы. Поскольку можно обнаружить, что вы запускаете один и тот же метод дважды, и мы уже знаем, что этот метод дорогой/длительный (или вам вообще не понадобится DoEvents()), даже если вы учли все упомянутые внешние зависимости выше, поэтому нет побочных эффектов, вы все равно можете дублировать большую работу.
    3. Другая проблема - крайняя версия первой: потенциальная тупиковая ситуация. Если что-то еще в вашей программе зависит от завершения вашего процесса и будет блокироваться до тех пор, пока это не произойдет, и эта вещь вызывается насосом сообщений из DoEvents(), ваше приложение застрянет и перестанет отвечать на запросы. Это может показаться надуманным, но на практике это удивительно легко сделать случайно, а сбои очень трудно найти и отладить позже. Это является корнем некоторых зависших ситуаций приложения, которые вы, возможно, испытывали на своем компьютере.
  • Проблемы с юзабилити. Это побочные эффекты, возникающие из-за неправильного учета других опасностей. Здесь нет ничего нового, если вы правильно смотрите в других местах.

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

Реальность такова, что большую часть времени, по крайней мере в мире .Net, компонент BackgroundWorker почти так же прост, по крайней мере, если вы сделали это один или два раза, и он выполнит свою работу в безопасный способ. Совсем недавно шаблон асинхронного/ожидающего выполнения или использование Task может быть гораздо более эффективным и безопасным, без необходимости углубляться в полноценный многопоточный код на вашем компьютере. своя.

Ответ 2

Назад в 16-битные дни Windows, когда каждая задача разделяла один поток, единственный способ сохранить оперативную реакцию в узком цикле - DoEvents. Именно это немодальное использование обескураживается в пользу потоков. Вот типичный пример:

' Process image
For y = 1 To height
    For x = 1 to width
        ProcessPixel x, y
    End For
    DoEvents ' <-- DON'T DO THIS -- just put the whole loop in another thread
End For

Для модальных вещей (например, отслеживания всплывающих окон), вероятно, все будет в порядке.

Ответ 3

Возможно, я ошибаюсь, но мне кажется, что DoDragDrop и TrackPopupMenu являются довольно частыми случаями, поскольку они берут на себя интерфейс, поэтому не имеют проблемы с реентерацией (что я считаю основной причиной люди описывают DoEvents как "Зло" ).

Лично я не думаю, что полезно отклонить функцию как "Зло" - скорее объясните подводные камни, чтобы люди могли сами решить. В случае DoEvents есть редкие случаи, когда все еще разумно использовать его, например, пока отображается модальный диалог прогресса, когда пользователь не может взаимодействовать с остальной частью пользовательского интерфейса, поэтому нет проблемы с повторным подключением.

Конечно, если под "Злом" вы подразумеваете "то, что вы не должны использовать без полного понимания ловушек", то я согласен, что DoEvents является злым.