Как использовать DoEvents(), не будучи "злым"?
18 Mehrdad [2012-07-05 23:45:00]
Простой поиск DoEvents приводит к большому количеству результатов, которые приводят, в основном, к:
DoEvents- зло. Не используйте его. Вместо этого используйте threading.
Причины, как правило, приводятся ниже:
- Проблемы с возвратом
- Плохая производительность
- Проблемы с удобством использования (например, перетаскивание по отключенному окну)
Но некоторые известные функции Win32, такие как TrackPopupMenu и DoDragDrop, выполняют собственную обработку сообщений, чтобы поддерживать пользовательский интерфейс, как это делает DoEvents.
И все же, ни один из них, похоже, не сталкивается с этими проблемами (производительность, повторное включение и т.д.).
Как они это делают? Как избежать проблем, связанных с DoEvents? (Или они?)
windows visual-c++ winapi winforms
3 ответа
24 Решение Joel Coehoorn [2012-07-06 00:07:00]
DoEvents() опасен. Но я уверен, вы делаете много опасных вещей каждый день. Только вчера я отправил несколько взрывных устройств (будущих читателей: обратите внимание на исходную дату публикации относительно определенного американского праздника). С осторожностью мы можем иногда объяснять опасности. Конечно, это означает знание и понимание того, что опасны:
-
Проблемы с повторной записью. Здесь есть две опасности:
- Часть проблемы здесь связана с стеком вызовов. Если вы вызываете .DoEvents() в цикле, который сам обрабатывает сообщения, которые используют DoEvents() и т.д., Вы получаете довольно глубокий стек вызовов. Легко переопределить DoEvents() и случайно заполнить стек вызовов, в результате чего возникает исключение StackOverflow. Если вы используете только .DoEvents() в одном или двух местах, вы, вероятно, все в порядке. Если это первый инструмент, к которому вы обращаетесь, когда у вас есть длительный процесс, вы можете легко найти себе проблемы. Даже одно использование в неправильном месте может позволить пользователю принудительно исключить исключение stackoverflow (иногда просто удерживая клавишу ввода), и это может быть проблемой безопасности.
- Иногда можно найти один и тот же метод в стеке вызовов дважды. Если вы не строили метод с учетом этого (подсказка: вы, вероятно, не сделали), тогда могут произойти плохие вещи. Если все, что передается методу, является типом значений, и нет никакой зависимости от вещей, находящихся за пределами метода, вы можете быть в порядке. Но в противном случае вам нужно тщательно подумать о том, что произойдет, если весь ваш метод будет запущен снова до того, как управление будет возвращено вам в точке, где вызывается .DoEvents(). Какие параметры или ресурсы вне вашего метода могут быть изменены, чего вы не ожидали? Изменяет ли ваш метод какие-либо объекты, где оба экземпляра в стеке могут воздействовать на один и тот же объект?
-
Проблемы с производительностью. DoEvents() может дать иллюзию многопоточности, но это не настоящая mutlithreading. Это имеет по крайней мере три настоящие опасности:
- Когда вы вызываете DoEvents(), вы передаете управление своим существующим потоком обратно на насос сообщений. Насос сообщения, в свою очередь, может контролировать что-то еще, и что-то другое может занять некоторое время. В результате ваша первоначальная операция может занять гораздо больше времени, чем если бы она была в потоке сама по себе, которая никогда не дает контроля, определенно дольше, чем нужно.
- Дублирование работы. Поскольку вы можете дважды запустить один и тот же метод, и мы уже знаем, что этот метод дорогостоящий/долговременный (или вам не обязательно нужен DoEvents()), даже если вы учли все внешние зависимости, упомянутые выше, поэтому никаких побочных эффектов нет, вы все равно можете дублировать много работы.
- Другая проблема - это крайняя версия первой: потенциал для тупика. Если что-то еще в вашей программе зависит от завершения процесса и будет блокироваться до тех пор, пока это не произойдет, и эта вещь вызывается насосом сообщений из DoEvents(), ваше приложение застрянет и перестанет отвечать на запросы. Это может показаться надуманным, но на практике это удивительно легко сделать случайно, и аварии очень трудно найти и отладить позже. Это находится в основе некоторых ситуаций с зависанием приложений, которые могут возникнуть на вашем собственном компьютере.
-
Проблемы с удобством использования. Это побочные эффекты, которые возникают из-за неправильного учета других опасностей. Здесь нет ничего нового, если вы смотрели в других местах соответственно.
Если, вы можете быть уверены, что учли все эти вещи, а затем продолжайте. Но действительно, если DoEvents() - это первое место, которое вы ищете для решения проблем реагирования/обновления пользовательского интерфейса, вы, вероятно, не учитываете все эти проблемы правильно. Если это не первое место, которое вы смотрите, есть еще много других вариантов, которые я бы поставил под вопрос, как вы сделали это с учетом DoEvents().
Реальность такова, что большую часть времени, по крайней мере, в мире .Net, BackgroundWorker, почти так же легко, по крайней мере, один раз, когда вы это сделали один или два раза, и это сделает работу безопасным способом. Совсем недавно шаблон async/await может быть намного более эффективным и безопасным.
2 Gabe [2012-07-05 23:54:00]
Назад в 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
Для модальных вещей (например, отслеживания всплывающих окон), вероятно, все будет в порядке.
2 Joe [2012-07-06 00:04:00]
Возможно, я ошибаюсь, но мне кажется, что DoDragDrop и TrackPopupMenu являются довольно частыми случаями, поскольку они берут на себя интерфейс, поэтому не имеют проблемы с реентерацией (что я считаю основной причиной люди описывают DoEvents как "Зло" ).
Лично я не думаю, что полезно отклонить функцию как "Зло" - скорее объясните подводные камни, чтобы люди могли сами решить. В случае DoEvents есть редкие случаи, когда все еще разумно использовать его, например, пока отображается модальный диалог прогресса, когда пользователь не может взаимодействовать с остальной частью пользовательского интерфейса, поэтому нет проблемы с повторным подключением.
Конечно, если под "Злом" вы подразумеваете "то, что вы не должны использовать без полного понимания ловушек", то я согласен, что DoEvents является злым.