Когда опрос по событиям будет лучше, чем использование шаблона наблюдателя?


41

Существуют ли сценарии, в которых опрос событий был бы лучше, чем использование шаблона наблюдателя ? У меня есть страх перед использованием опроса, и я бы начал его использовать, только если бы кто-то дал мне хороший сценарий. Все, о чем я могу думать, - это то, как шаблон наблюдателя лучше, чем опрос. Рассмотрим этот сценарий:

Вы программируете симулятор автомобиля. Машина это объект. Как только машина включится, вы захотите воспроизвести аудиоклип "vroom vroom".

Вы можете смоделировать это двумя способами:

Опрос : Опрашивайте объект автомобиля каждую секунду, чтобы увидеть, включен ли он. Когда он включен, воспроизведите звуковой клип.

Образец наблюдателя : Сделайте автомобиль Субъектом образца наблюдателя. Пусть оно опубликует событие «on» всем наблюдателям, когда оно само включится. Создайте новый звуковой объект, который слушает машину. Пусть он реализует обратный вызов «on», который воспроизводит аудиоклип.

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


Я не могу придумать почти никакого сценария. Шаблон наблюдателя - это то, что на самом деле отображается в реальном мире и реальной жизни. Таким образом, я думаю, что ни один сценарий никогда не оправдывает его неиспользование.
Саид Нимати

Вы говорите о событиях пользовательского интерфейса или событиях в целом?
Брайан Оукли

3
Ваш пример не устраняет проблему опроса / наблюдения. Вы просто перешли на более низкий уровень. Ваша программа все еще должна выяснить, включена машина или нет каким-то механизмом.
Данк

Ответы:


55

Представьте, что вы хотите получать уведомления о каждом цикле работы двигателя, например, отображать измерение оборотов для водителя.

Шаблон наблюдателя: Механизм публикует событие «цикл двигателя» всем наблюдателям для каждого цикла. Создайте прослушиватель, который считает события и обновляет отображение RPM.

Опрос: Дисплей оборотов в минуту запрашивает у двигателя счетчик циклов двигателя и соответственно обновляет дисплей оборотов.

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


PS: Я также часто использую шаблон опроса в распределенном программировании:

Шаблон наблюдателя: Процесс A отправляет сообщение процессу B, в котором говорится, что «каждый раз, когда происходит событие E, отправляйте сообщение процессу A».

Шаблон опроса: процесс A регулярно отправляет сообщение процессу B, в котором говорится: «Если событие E произошло с момента последнего опроса, отправьте мне сообщение сейчас».

Шаблон опроса производит немного большую нагрузку на сеть. Но у модели наблюдателя есть и недостатки:

  • Если происходит сбой процесса A, он никогда не откажется от подписки, и процесс B будет пытаться отправлять ему уведомления навсегда, если только он не сможет надежно обнаружить сбои удаленного процесса (это не легко сделать)
  • Если событие E является очень частым и / или уведомления содержат много данных, то процесс A может получить больше уведомлений о событиях, чем он может обработать. С помощью шаблона опроса он может просто задушить опрос.
  • В схеме наблюдателя высокая нагрузка может вызывать «пульсации» по всей системе. Если вы используете блокирующие розетки, эта рябь может идти обоими путями.

1
Хорошая точка зрения. Иногда лучше проводить опрос ради производительности.
Сокол

1
Ожидаемое количество наблюдателей также является соображением. Когда вы ожидаете большое количество наблюдателей, обновление их всех из наблюдаемого может стать узким местом производительности. Гораздо проще тогда просто написать где-нибудь значение и попросить «наблюдателей» проверить это значение, когда оно им нужно.
Марьян Венема

1
«если он не может надежно обнаруживать сбои удаленного процесса (это не легко сделать)» ... за исключением опроса; P. Таким образом, лучший дизайн - минимизировать реакцию «ничего не изменилось». +1, хороший ответ.
фунтовые

2
@Jojo: Вы могли бы, да, но тогда вы помещаете политику, которая должна отображаться на дисплее, в счетчик RPM. Возможно, пользователь иногда хочет иметь высокоточный дисплей RPM.
Зан Рысь

2
@JoJo: публикация каждого сотого события - это взлом. Это работает только в том случае, если частота события всегда находится в правильном диапазоне, если обработка события не занимает слишком много времени для механизма, если все подписчики нуждаются в сопоставимой точности. И для этого требуется одна операция модуля на оборот в минуту, что (при условии нескольких тысяч оборотов в минуту) для ЦП требует гораздо больше работы, чем несколько операций опроса в секунду.
nikie

7

Опрос лучше, если процесс опроса проходит значительно медленнее, чем опрашиваемые объекты. Если вы записываете события в базу данных, часто лучше опросить всех производителей событий, собрать все события, произошедшие с момента последнего опроса, а затем записать их в одну транзакцию. Если вы попытаетесь записать каждое событие в том виде, как оно произошло, вы не сможете поддерживать его, и в конечном итоге у вас возникнут проблемы, когда ваши входные очереди заполнятся. Это также имеет больше смысла в слабосвязанных распределенных системах, где задержка высока или установка и разрыв соединения стоят дорого. Я считаю, что системы опросов легче писать и понимать, но в большинстве ситуаций наблюдатели или потребители, управляемые событиями, похоже, предлагают более высокую производительность (по моему опыту).


7

Опрос намного проще заставить работать по сети, когда соединения могут перестать работать, серверы могут работать и т. Д. Помните, что в конце дня сокет TCP нуждается в «опросе» сообщений keep-a-live, иначе сервер примет клиента ушел

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

При условии, что сервер может ответить «без изменений» по очень низкой цене, и вы не будете опрашивать слишком часто, и у вас не будет тысяч опросов клиентов, тогда опрос будет очень хорошо работать в реальной жизни.

Однако для случаев « в памяти » я по умолчанию использую шаблон наблюдателя, поскольку он обычно является наименьшей работой.


5

У опроса есть некоторые недостатки, вы в основном указали их уже в своем вопросе.

Однако это может быть лучшим решением, когда вы хотите действительно отделить наблюдаемое от любых наблюдателей. Но иногда может быть лучше использовать наблюдаемую оболочку для объекта, который будет наблюдаться в таких случаях.

Я бы использовал опрос только тогда, когда наблюдаемое не может наблюдаться при взаимодействии объектов, что часто бывает, например, при запросах к базам данных, когда у вас не может быть обратных вызовов. Другой проблемой может быть многопоточность, где зачастую безопаснее опрашивать и обрабатывать сообщения, а не вызывать объекты напрямую, чтобы избежать проблем параллелизма.


Я не совсем уверен, почему вы считаете опрос безопаснее для многопоточности. В большинстве случаев это не так. Когда обработчик опроса получает запрос опроса, он должен был выяснить состояние опрашиваемого объекта, если объект находится в процессе обновления, это не безопасно для обработчика опроса. В сценарии прослушивателя вы получаете уведомления только в том случае, если толкатель находится в согласованном состоянии, поэтому вы можете избежать большинства проблем с синхронизацией в объекте опроса.
Ли Райан

4

Для хорошего примера того, как опрос вступает во владение от уведомления, посмотрите на сетевые стеки операционной системы.

Для Linux было большой проблемой, когда сетевой стек включил NAPI, сетевой API, который позволял драйверам переключаться из режима прерывания (уведомления) в режим опроса.

При наличии нескольких гигабитных интерфейсов Ethernet прерывания часто перегружают ЦП, в результате чего система работает медленнее, чем следовало бы. При опросе сетевые карты собирают пакеты в буферах до тех пор, пока они не будут опрошены, или карты даже записывают пакеты в память через DMA. Затем, когда операционная система готова, она опрашивает карту на предмет всех своих данных и выполняет стандартную обработку TCP / IP.

Режим опроса позволяет ЦПУ собирать данные Ethernet с максимальной скоростью обработки без бесполезной нагрузки прерывания. Режим прерывания позволяет процессору бездействовать между пакетами, когда работа не так занята.

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


2

Я люблю опросы! Я? Да! Я? Да! Я? Да! Я все еще? Да! А сейчас? Да!

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

Но главная причина, по которой я люблю опрос, заключается в его простоте и предсказуемости. Вы можете отследить код и легко увидеть, когда и где что-то произойдет, и в каком потоке. Теоретически, если бы мы жили в мире, где опрос был незначительной тратой (хотя реальность была далека от этого), то я считаю, что это упростит поддержание кода огромной сделкой. И в этом заключается преимущество опроса и вытягивания, так как я вижу, можем ли мы игнорировать производительность, даже если в этом случае мы не должны этого делать.

Когда я начал программировать в эпоху DOS, мои маленькие игры вращались вокруг опроса. Я скопировал некоторый ассемблерный код из книги, которую я едва понимал в отношении прерываний клавиатуры, и заставил ее хранить буфер состояний клавиатуры, и в этот момент мой основной цикл всегда опрашивал. Клавиша вверх вниз? Нет. Клавиша вверх вниз? Нет. Как насчет сейчас? Нет. В настоящее время? Да. Хорошо, переместите игрока.

И хотя это невероятно расточительно, я обнаружил, что гораздо легче рассуждать по сравнению с сегодняшними днями многозадачного и событийного программирования. Я точно знал, когда и где все будет происходить, и было проще поддерживать стабильную и предсказуемую частоту кадров без сбоев.

Таким образом, с тех пор я всегда пытался найти способ получить некоторые из преимуществ и предсказуемости этого, фактически не сжигая циклы ЦП, например, используя условные переменные, чтобы уведомить потоки о пробуждении, в какой момент они могут получить новое состояние, делай свое дело и возвращайся спать в ожидании, чтобы получить уведомление снова.

И каким-то образом я нахожу, что с очередями событий работать намного легче, по крайней мере, с шаблонами наблюдателей, даже несмотря на то, что они все еще не позволяют предсказать, куда вы в конечном итоге попадете или что в итоге произойдет. Они, по крайней мере, централизуют поток управления обработкой событий в нескольких ключевых областях системы и всегда обрабатывают эти события в одном и том же потоке, вместо того, чтобы отскочить от одной функции к где-то полностью удаленному и неожиданному за пределами центрального потока обработки событий. Таким образом, дихотомия не всегда должна быть между наблюдателями и опросами. Очереди событий - своего рода золотая середина там.

Но да, почему-то мне гораздо проще рассуждать о системах, которые делают вещи, которые аналогичным образом похожи на предсказуемые потоки управления, которые у меня были, когда я проводил опрос много лет назад, в то же время просто противодействуя тенденции к работе в времена, когда никаких изменений состояния не произошло. Так что есть преимущество, если вы можете делать это так, чтобы не сжигать циклы ЦП без необходимости, как с условными переменными.

Гомогенные петли

Хорошо, я получил отличный комментарий, Josh Caswellкоторый указал на некоторую глупость в моем ответе:

«как использование условных переменных для оповещения потоков о пробуждении» Похоже на организацию событий / наблюдателей, а не опрос

Технически сама переменная условия применяет шаблон наблюдателя для пробуждения / уведомления потоков, поэтому называть этот «опрос», вероятно, невероятно вводящим в заблуждение. Но я считаю, что это дает такое же преимущество, которое я нашел, как опрос в дни DOS (только с точки зрения потока управления и предсказуемости). Я постараюсь объяснить это лучше.

В те дни я находил привлекательным то, что вы можете посмотреть на фрагмент кода или проследить его и сказать: «Хорошо, весь этот раздел посвящен обработке событий клавиатуры. В этом разделе кода больше ничего не произойдет. И я точно знаю, что произойдет раньше, и я точно знаю, что произойдет после (например, физика и рендеринг). " Опрос состояний клавиатуры дал вам такую ​​централизацию потока управления для обработки того, что должно происходить в ответ на это внешнее событие. Мы не сразу отреагировали на это внешнее событие. Мы ответили на это, когда нам будет удобно.

Когда мы используем систему на основе push, основанную на шаблоне Observer, мы часто теряем эти преимущества. Элемент управления может быть изменен, что вызывает событие изменения размера. Когда мы прослеживаем его, мы обнаруживаем, что мы находимся внутри экзотического элемента управления, который делает много пользовательских вещей при его изменении размера, что вызывает больше событий. В итоге мы полностью удивляемся, прослеживая во всех этих каскадных событиях то, где мы оказались в системе. Кроме того, мы могли бы обнаружить, что все это даже не происходит последовательно в каком-либо конкретном потоке, потому что поток A может изменить размер элемента управления здесь, в то время как поток B также изменит размер элемента управления позже. Поэтому мне всегда было очень трудно рассуждать о том, насколько сложно предсказать, где все происходит, а что произойдет.

Мне немного проще рассуждать об очереди событий, потому что она упрощает, где все это происходит, по крайней мере, на уровне потока. Однако может произойти много разнородных вещей. Очередь событий может содержать эклектичную смесь событий, которые нужно обработать, и каждое из них может удивить нас каскадом произошедших событий, порядком их обработки и тем, как мы в конечном итоге подпрыгиваем повсюду в кодовой базе. ,

То, что я считаю «ближайшим» к опросу, не будет использовать очередь событий, но откладывает очень однородный тип обработки. A PaintSystemможет быть предупрежден через переменную условия, что есть работа по рисованию, чтобы перерисовать определенные ячейки сетки окна, после чего он выполняет простой последовательный цикл по ячейкам и перерисовывает все внутри него в правильном z-порядке. Здесь может быть один уровень вызова косвенной / динамической диспетчеризации, чтобы вызвать события рисования в каждом виджете, находящемся в ячейке, которую нужно перекрасить, но это все - только один уровень косвенных вызовов. Переменная условия использует шаблон наблюдателя, чтобы предупредить, что в этот моментPaintSystem что у него есть работа, но она не указывает ничего более этого, иPaintSystemпосвящена одной единой, очень однородной задаче на тот момент. Когда мы отлаживаем и отслеживаем PaintSystem'sкод, мы знаем, что ничего не произойдет, кроме рисования.

Таким образом, это в основном сводит систему к тому, что эти вещи выполняют однородные циклы над данными, применяя очень особую ответственность за них, а не неоднородные циклы над разнородными типами данных, выполняющими многочисленные обязанности, которые мы могли бы получить при обработке очереди событий.

Мы стремимся к этому типу вещей:

when there's work to do:
   for each thing:
       apply a very specific and uniform operation to the thing

В отличие от:

when one specific event happens:
    do something with relevant thing
in relevant thing's event:
    do some more things
in thing1's triggered by thing's event:
    do some more things
in thing2's event triggerd by thing's event:
    do some more things:
in thing3's event triggered by thing2's event:
    do some more things
in thing4's event triggered by thing1's event:
    cause a side effect which shouldn't be happening
    in this order or from this thread.

И так далее. И это не должно быть один поток на задачу. Один поток может применять логику макетов (изменение размера / перемещение) для элементов управления графическим интерфейсом и перерисовывать их, но он может не обрабатывать щелчки клавиатуры или мыши. Таким образом, вы можете рассматривать это как улучшение однородности очереди событий. Но нам не нужно использовать очередь событий и чередовать функции изменения размера и рисования. Мы можем сделать как:

in thread dedicated to layout and painting:
    when there's work to do:
         for each widget that needs resizing/reposition:
              resize/reposition thing to target size/position
              mark appropriate grid cells as needing repainting
         for each grid cell that needs repainting:
              repaint cell
         go back to sleep

Таким образом, вышеприведенный подход просто использует условную переменную, чтобы уведомить поток, когда есть работа, но он не чередует различные типы событий (изменение размера в одном цикле, рисование в другом цикле, а не смесь обоих), и он не ' не пытайтесь сообщить, что именно нужно сделать, чтобы выполнить работу (поток «обнаруживает» это после пробуждения, просматривая общесистемные состояния ECS). Каждый цикл, который он выполняет, имеет очень однородный характер, что позволяет легко рассуждать о порядке, в котором все происходит.

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

Затем этот тип окончательного многопоточного дизайна графического интерфейса позволил мне придумать что-то еще, о чем было бы гораздо проще рассуждать, и избегать тех типов ошибок, которые я имел в виду, и одна из причин, по которой мне было гораздо проще рассуждать в меньше всего из-за этих однородных циклов и того, как они напоминают поток управления, похожий на тот, что я делал в дни DOS (хотя это не совсем опрос, а выполнение работы только тогда, когда есть работа, которую нужно выполнить). Идея заключалась в том, чтобы отойти как можно дальше от модели обработки событий, которая подразумевает неоднородные циклы, неоднородные побочные эффекты, неоднородные потоки управления, и все больше и больше работать в направлении однородных циклов, работающих равномерно на однородных данных и изолирующих и объединение побочных эффектов таким образом, чтобы легче было просто сосредоточиться на «что»


1
«как использование условных переменных для уведомления потоков о пробуждении» Похоже на организацию событий / наблюдателей, а не опрос.
Джош Касвелл

Я нахожу разницу очень тонкой, но уведомление просто в форме "Есть работа, которую нужно сделать", чтобы пробудить темы. Например, при изменении размера родительского элемента управления шаблон наблюдателя может каскадно изменять размеры вызовов по иерархии с использованием динамической диспетчеризации. Вещи должны были бы косвенно вызывать свои функции события изменения размера. Тогда они могут перекрасить себя немедленно. Затем, если мы используем очередь событий, изменение размера родительского элемента управления может привести к перемещению событий изменения размера вниз по иерархии, после чего функции изменения размера для каждого

... указывают, что они могут затем выдвигать события перерисовки, которые, аналогичным образом, вызываются отложенным образом после того, как все закончило изменение размера, и все из центрального потока обработки событий. И я считаю, что централизация немного выгодна, по крайней мере, в том, что касается отладки и возможности легко рассуждать о том, где происходит обработка (включая какой поток) ... Тогда то, что я считаю ближайшим к опросу, не является эти решения ...

Например, было бы иметь a, LayoutSystemкоторый обычно спит, но когда пользователь изменяет размер элемента управления, он будет использовать переменную условия для пробуждения LayoutSystem. Затем LayoutSystemизменяет размеры всех необходимых элементов управления и возвращается в режим сна. При этом прямоугольные области, в которых находятся виджеты, помечаются как нуждающиеся в обновлении, после чего PaintSystemпробуждение и прохождение через эти прямоугольные области перерисовываются те, которые необходимо перерисовать в плоском последовательном цикле.

Таким образом, сама переменная условия следует шаблону наблюдателя, чтобы уведомить потоки о пробуждении, но мы не передаем никакой информации, кроме «есть работа, которую нужно сделать». И каждая просыпающаяся система предназначена для обработки вещей в очень простом цикле с применением очень однородной задачи, в отличие от очереди событий, которая имеет неоднородные задачи (она может содержать эклектичную смесь событий для обработки).

-4

Я даю вам обзор более концептуального мышления о скороговорке наблюдателя. Подумайте о сценарии, как подписка на каналы YouTube. Есть количество пользователей, которые подписаны на канал, и как только на канале будет какое-либо обновление, которое состоит из множества видео, подписчик получает уведомление об изменении в этом конкретном канале. Таким образом, мы пришли к выводу, что если канал является СУБЪЕКТОМ, у которого есть возможность подписаться, отмените подписку и уведомите всех НАБЛЮДАТЕЛЕЙ, которые зарегистрированы в канале.


2
это даже не попытка ответить на заданный вопрос, когда опрос по событиям будет лучше, чем использование шаблона наблюдателя. Смотрите, как ответить
gnat
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.