Как использовать DoEvents(), не будучи "злым"?
Простой поиск DoEvents
приводит множество результатов, которые в основном приводят к:
DoEvents
это зло Не используйте это. Вместо этого используйте потоки.
Причины, которые обычно приводятся ниже:
- Проблемы с повторным входом
- Низкая производительность
- Проблемы с юзабилити (например, перетаскивание через отключенное окно)
Но некоторые известные функции Win32, такие как TrackPopupMenu
а также DoDragDrop
выполнять собственную обработку сообщений, чтобы пользовательский интерфейс реагировал так же, как DoEvents
делает.
И все же ни один из них, похоже, не сталкивался с этими проблемами (производительность, повторный вход и т. Д.).
Как они это делают? Как они избегают проблем, процитированных с DoEvents
? (Или они?)
3 ответа
DoEvents() опасно. Но держу пари, ты каждый день делаешь много опасных вещей. Буквально вчера я запустил несколько взрывных устройств (будущие читатели: обратите внимание на оригинальную дату публикации относительно определенного американского праздника). С осторожностью мы иногда можем объяснить опасности. Конечно, это значит знать и понимать, в чем опасность:
Вопросы повторного входа. Здесь на самом деле есть две опасности:
- Часть проблемы здесь связана со стеком вызовов. Если вы вызываете.DoEvents() в цикле, который сам обрабатывает сообщения, использующие DoEvents() и т. Д., Вы получаете довольно глубокий стек вызовов. Легко использовать DoEvents() и случайно заполнить ваш стек вызовов, что приведет к исключению Stackru. Если вы используете.DoEvents() только в одном или двух местах, вы, вероятно, в порядке. Если это первый инструмент, к которому вы обращаетесь, когда у вас есть длительный процесс, вы можете легко оказаться здесь в беде. Даже одно использование в неправильном месте может позволить пользователю вызвать исключение переполнения стека (иногда просто удерживая клавишу ввода), и это может быть проблемой безопасности.
- Иногда можно найти один и тот же метод в стеке вызовов дважды. Если вы не создали метод с учетом этого (подсказка: вы, вероятно, не сделали), тогда могут произойти плохие вещи. Если все, что передано в метод, является типом значения, и нет никакой зависимости от вещей вне метода, то вы можете быть в порядке. Но в противном случае вам нужно тщательно подумать о том, что произойдет, если весь ваш метод снова запустится до того, как вам вернут элемент управления в тот момент, когда вызывается.DoEvents(). Какие параметры или ресурсы вне вашего метода могут быть изменены, чего вы не ожидали? Меняет ли ваш метод какие-либо объекты, где оба экземпляра в стеке могут действовать на один и тот же объект?
Проблемы с производительностью. DoEvents() может создать иллюзию многопоточности, но это не настоящая многопоточность. Это имеет как минимум три настоящие опасности:
- Когда вы вызываете DoEvents(), вы передаете управление существующим потоком обратно в насос сообщений. Насос сообщений может, в свою очередь, передать управление чему-то другому, и это может занять некоторое время. В результате ваша оригинальная операция может занять гораздо больше времени, чем если бы она находилась в потоке сама по себе, который никогда не даст управление, определенно дольше, чем нужно.
- Дублирование работы. Поскольку можно обнаружить, что он запускает один и тот же метод дважды, и мы уже знаем, что этот метод дорогой / длительный (или вам вообще не понадобится DoEvents()), даже если вы учли все упомянутые внешние зависимости выше, поэтому нет никаких побочных эффектов, вы все равно можете повторить много работы.
- Другая проблема - крайняя версия первой: возможность тупика. Если что-то еще в вашей программе зависит от завершения вашего процесса и будет блокироваться до тех пор, пока это не произойдет, и эта вещь вызывается насосом сообщений из DoEvents(), ваше приложение застревает и перестает отвечать на запросы. Это может показаться надуманным, но на практике это удивительно легко сделать случайно, а сбои очень трудно найти и отладить позже. Это является корнем некоторых зависших ситуаций приложения, которые вы, возможно, испытывали на своем компьютере.
Вопросы юзабилити. Это побочные эффекты, которые возникают из-за неправильного учета других опасностей. Здесь нет ничего нового, если вы правильно смотрели в других местах.
Если вы можете быть уверены, что учли все эти вещи, тогда продолжайте. Но на самом деле, если DoEvents() - первое место, куда вы обращаетесь для решения проблем с отзывчивостью и обновлением пользовательского интерфейса, вы, вероятно, неправильно учитываете все эти проблемы. Если вы смотрите не в первый раз, есть достаточно других вариантов, и я бы задал вопрос, как вы попали к DoEvents() вообще.
Реальность такова, что большую часть времени, по крайней мере в мире.Net, компонент BackgroundWorker почти так же прост, по крайней мере, если вы сделали это один или два раза, и он будет выполнять работу безопасным способом. Совсем недавно, шаблон асинхронного / ожидающего или использование Task
может быть гораздо более эффективным и безопасным.
Еще в 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
Для модальных вещей (таких как отслеживание всплывающих окон), скорее всего, все будет в порядке.
Я могу ошибаться, но мне кажется, что DoDragDrop
а также TrackPopupMenu
это довольно особые случаи, в которых они принимают пользовательский интерфейс, поэтому не имеют проблемы с повторным входом (я думаю, это основная причина, по которой люди описывают DoEvents
как "Зло").
Лично я не думаю, что было бы полезно отклонить функцию как "Зло" - скорее объясните ловушки, чтобы люди могли сами решать. В случае DoEvents
Есть редкие случаи, когда его все еще целесообразно использовать, например, когда отображается модальное диалоговое окно прогресса, где пользователь не может взаимодействовать с остальным пользовательским интерфейсом, поэтому нет проблем с повторным входом.
Конечно, если под "Злом" вы подразумеваете "что-то, что не следует использовать без полного понимания ловушек", то я согласен с тем, что DoEvents
это зло