Коли опитування подій було б краще, ніж використання шаблону спостерігачів?


41

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

Ви програмуєте автомобільний тренажер. Автомобіль - предмет. Як тільки машина включиться, ви хочете відтворити звуковий кліп "vroom vroom".

Можна моделювати це двома способами:

Опитування : оглядайте автомобільний предмет щосекунди, щоб побачити, чи він увімкнено. Коли він увімкнено, відтворіть звуковий кліп.

Шаблон спостерігача : зробіть автомобіль предметом шаблону спостерігача. Нехай вона публікує подію "увімкнено" всім спостерігачам, коли вона сама включається. Створіть новий звуковий об’єкт, який слухатиме машину. Нехай він реалізує зворотний дзвінок "увімкнено", який відтворює звуковий кліп.

У цьому випадку я думаю, що модель спостерігача виграє. По-перше, опитування є більш інтенсивним процесором. По-друге, звуковий кліп не одразу спрацьовує, коли автомобіль включається. Через період опитування може бути розрив до 1 секунди.


Я не можу придумати майже жодного сценарію. Шаблон спостерігача - це те, що насправді відображає реальний світ та реальне життя. Тому я думаю, що жоден сценарій ніколи не виправдає його використання.
Saeed Neamati

Ви говорите про події інтерфейсу користувача чи події взагалі?
Брайан Оуклі

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

Відповіді:


55

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

Шаблон спостерігача: двигун публікує подію "цикл двигуна" всім спостерігачам за кожен цикл. Створіть слухача, який підраховує події та оновлює дисплей RPM.

Опитування: Дисплей RPM запитує двигун через регулярні проміжки часу для лічильника циклу двигуна та оновлює відповідний дисплей RPM.

У цьому випадку модель спостереження, ймовірно, втрачається: цикл двигуна - це процес високої частоти, з пріоритетним рівнем, ви не хочете затримувати або зупиняти цей процес лише для оновлення дисплея. Ви також не хочете обробляти пул потоків за допомогою подій циклу двигуна.


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

Шаблон спостерігача: Процес A посилає повідомлення на процес B, в якому йдеться про те, що "щоразу, коли трапляється подія E, надсилайте повідомлення Процесу A".

Шаблон опитування: Процес A регулярно надсилає повідомлення на процес B, в якому сказано, "якщо ви подія E сталася з моменту останнього опитування, надішліть мені повідомлення зараз".

Шаблон опитування створює трохи більше навантаження на мережу. Але у спостерігача також є і мінуси:

  • Якщо процес A завершиться збоєм, він ніколи не скасовує підписку, і процес B намагатиметься надсилати йому сповіщення на всю вічність, якщо тільки він не зможе надійно виявити віддалені збої процесу (це не просто справа)
  • Якщо подія Е дуже часта і / або сповіщення несуть багато даних, то обробка A може отримати більше сповіщень про події, ніж вона може обробляти. За схемою опитування він може просто придушити опитування.
  • За схемою спостереження велике навантаження може спричинити «брижі» через всю систему. Якщо ви використовуєте блокуючі розетки, ці пульсації можуть йти обома способами.

1
Влучне зауваження. Іноді краще занадто опитуватись задля виконання.
Сокіл

1
Очікувана кількість спостерігачів також враховує. Коли ви очікуєте великої кількості спостерігачів, оновлення їх усіх від спостережуваного може стати вузьким місцем. Набагато простіше, ніж просто записати значення десь і змусити "спостерігачів" перевірити це значення, коли воно потребує.
Мар'ян Венема

1
"якщо тільки він не може надійно виявити віддалені збої процесу (це непроста річ)" ... за винятком опитування; P. Таким чином, найкращий дизайн - максимально мінімізувати відповідь "нічого не змінилося". +1, хороша відповідь.
пдр

2
@Jojo: Можна, так, але тоді ви вносите політику, яка повинна належати до дисплея, в лічильник RPM. Можливо, користувач час від часу хоче мати високоточний дисплей в обороті.
Zan Lynx

2
@JoJo: Публікування кожної 100-ї події - це хакер. Він працює добре лише тоді, коли частота подій завжди знаходиться в потрібному діапазоні, якщо обробка події не забирає занадто багато часу для двигуна, якщо всім абонентам потрібна порівнянна точність. І це займає одну модульну операцію на RPM, що (припускаючи кілька тисяч RPM) - це набагато більше роботи для процесора, ніж кілька операцій опитування в секунду.
nikie

7

Опитування краще, якщо процес опитування протікає значно повільніше, ніж опитування. Якщо ви записуєте події в базу даних, часто краще опитати всіх виробників подій, зібрати всі події, що відбулися з моменту останнього опитування, а потім записати їх однією транзакцією. Якщо ви намагалися записати кожну подію так, як вона сталася, ви, можливо, не зможете не відставати і, зрештою, виникнуть проблеми при заповненні черг на введення. Це також має більше сенсу в розподілених системах з обмеженою сполученістю, де висока затримка або налаштування підключення та розрив дорогі. Мені здається, що системи опитування легше писати та розуміти, але в більшості ситуацій спостерігачі або споживачі, орієнтовані на події, здаються, пропонують кращу ефективність (на мій досвід).


7

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

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

За умови, що сервер може відповісти "без змін" дуже низькою ціною, і ви не опитуєтесь занадто часто, і у вас немає 1000 тисяч клієнтів, тоді опитування працює дуже добре в реальному житті.

Однак для випадків " у пам'яті " я за замовчуванням використовую шаблон спостерігача, оскільки це, як правило, найменш робота.


5

Опитування має деякі недоліки, ви, в основному, їх уже вказали у своєму запитанні.

Однак це може бути кращим рішенням, коли ви хочете по-справжньому відключити спостережуване від будь-яких спостерігачів. Але іноді може бути краще використовувати спостережну обгортку для об'єкта, який слід спостерігати в таких випадках.

Я б використовував опитування лише тоді, коли спостережуваного не можна спостерігати при взаємодії об'єктів, що часто трапляється при запитах баз даних, наприклад, коли у вас не може бути зворотних викликів. Інша проблема може бути багатопотоковою, де часто буває безпечніше опитувати та обробляти повідомлення, а не викликати безпосередньо об’єкти, щоб уникнути проблем з одночасністю.


Я не зовсім впевнений, чому ви вважаєте, що опитування є більш безпечним для багаторівневої нарізки. У більшості випадків це не буде так. Коли обробник опитування отримує запит на опитування, він повинен був з'ясувати стан опитуваного об'єкта, якщо об'єкт знаходиться в середині оновлення, то це не безпечно для обробника опитування. У сценарії прослуховування ви отримуєте сповіщення лише в тому випадку, якщо штовхач знаходиться в постійному стані, тому ви можете уникнути більшості проблем синхронізації в опитуваному об'єкті.
Лежати Райан

4

Для гарного прикладу, коли опитування бере участь у повідомленні, подивіться на стеки мереж операційної системи.

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

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

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

Секрет - коли перейти з одного режиму в інший. Кожен режим має переваги і його слід використовувати в належному місці.


2

Я люблю опитування! Я? Так! Я? Так! Я? Так! Я ще? Так! А зараз? Так!

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

Але головна причина, що я люблю опитування, - через її простоту та передбачуваний характер. Ви можете простежити код і легко побачити, коли і де відбуватимуться справи, і в якій нитці. Якщо б теоретично ми жили у світі, де опитування було незначним відходом (хоч і реальність далека від нього), то я вважаю, що це спростить підтримання коду величезною угодою. І в цьому користь опитування та витягування, як я бачу, якби ми могли нехтувати ефективністю, навіть якщо в цьому випадку ми не повинні.

Коли я почав програмувати в епоху DOS, мої маленькі ігри оберталися навколо опитування. Я скопіював деякий код складання з книги, яку я ледве зрозумів, що стосується перерв на клавіатурі, і змусив її зберігати буфер станів клавіатури, і тоді мій головний цикл завжди опитував. Чи клавіша вгору вниз? Ні. Чи клавіша вгору вниз? Ні. Як щодо тепер? Ні. Тепер? Так. Гаразд, перемістіть плеєр.

І хоча це було надзвичайно марно, я виявив, що так набагато простіше міркувати порівняно з цими днями багатозадачність та керовані подіями програмування. Я точно знав, коли і де все відбуватиметься в будь-який час, і було легше тримати частоту кадрів стабільною та передбачуваною без гікавок.

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

І я якось знаходжу черги подій набагато простіше працювати з принаймні шаблонами спостерігачів, хоча вони все ще не дозволяють передбачити, куди ви в кінцевому підсумку виїдете або що в кінцевому підсумку відбудеться. Вони принаймні централізують потік управління обробкою подіями в декілька ключових областей системи і завжди обробляють ці події в одному потоці, замість того, щоб відскочити від однієї функції до кудись абсолютно віддаленого і несподіваного раптово поза центральною потоком обробки події. Тому дихотомія не завжди повинна бути між спостерігачами та опитуваннями. Черги на події - це свого роду середнє місце.

Але так, я чомусь мені набагато простіше міркувати про системи, які роблять речі, аналогічні ближчим до того, який передбачуваний потік управління, який я мав, коли я опитувався віками тому, просто протидіючи тенденції, коли робота відбуватиметься на часи, коли жодних змін у державі не відбулося. Отже, є ця користь, якщо ви можете це зробити так, що не записуючи цикли процесора без потреби, як зі змінними стану.

Однорідні петлі

Гаразд, я отримав чудовий коментар, Josh Caswellякий вказував на деяку шаленість у моїй відповіді:

"як, наприклад, використання змінних умов для сповіщення потоків для пробудження" Звучить як домовленість на основі подій / спостерігача, а не опитування

Технічно сама змінна умова застосовує модель спостерігача для пробудження / сповіщення потоків, тому називати, що "опитування", ймовірно, було б неймовірно оманливим. Але я вважаю, що це дає аналогічну перевагу, яку я виявив, як опитування в дні DOS (саме з точки зору потоку контролю та передбачуваності). Я спробую це пояснити краще.

У ті часи мені було привабливо те, що ви можете переглянути розділ коду або простежити його і сказати: «Гаразд, весь цей розділ присвячений обробці подій клавіатури. Нічого іншого в цьому розділі коду не відбудеться. І я точно знаю, що відбуватиметься раніше, і точно знаю, що буде після цього (фізика та візуалізація, наприклад) ". Опитування штатів клавіатури дало вам таку централізацію контрольного потоку, наскільки обходиться тим, що має продовжуватися у відповідь на цю зовнішню подію. На цю зовнішню подію ми відповіли не відразу. Ми відповіли на це в нашій зручності.

Коли ми використовуємо поштовхову систему, засновану на шаблоні спостерігача, ми часто втрачаємо ці переваги. Елемент управління може бути змінений, що викликає подію зміни розміру. Коли ми простежимо його, ми виявимо, що ми знаходимось в екзотичному контролі, який робить багато спеціальних речей у його зміні розміру, що викликає більше подій. Ми зрештою повністю здивовані, прослідковуючи всі ці каскадні події щодо того, де ми опиняємось у системі. Крім того, ми можемо виявити, що все це навіть не відбувається послідовно в жодному потоці, оскільки потік A може змінити розмір елемента управління, тоді як потік B також змінить елемент керування згодом. Тому мені завжди було важко міркувати про те, як важко передбачити, де все відбувається, а також що буде.

Черга подій мені трохи простіше міркувати, оскільки вона спрощує, де всі ці речі відбуваються принаймні на рівні потоку. Однак може відбуватися багато різного роду. Черга подій може містити еклектичну суміш подій, що обробляються, і кожна з них все ще може здивувати нас тим, який каскад подій, що відбулися, порядок їх обробки та як ми в кінцевому підсумку підстрибуємо по всьому місцю в кодовій базі .

Те, що я вважаю "найближчим" до опитування, не використовувало б черги подій, але відкладало б дуже однорідний тип обробки. PaintSystemМоже бути попереджено через змінне стан , що там картина роботи , щоб зробити перефарбовувати деякі осередки сітки з вікна, після чого він робить просту послідовну петлю через клітку і перемальовує все всередині нього в справному г- го порядку. Тут може бути один рівень виклику непрямості / динамічної диспетчеризації, щоб викликати події фарби у кожному віджеті, що знаходиться у комірці, яку потрібно перефарбувати, але це все - лише один шар непрямих викликів. Змінна умови використовує шаблон спостереження, щоб попереджати про 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

Отже, вищевказаний підхід просто використовує змінну умови для повідомлення потоку, коли потрібно виконати роботу, але він не переплітає різні типи подій (розмір в одному циклі, фарба в іншому циклі, а не суміш обох), і він не ' t не намагайтеся повідомити, яка саме робота повинна бути виконана (нитка "виявляє", що прокидається, дивлячись на загальносистемні стани ECS). Кожен цикл, який він виконує, є дуже однорідним за своєю природою, що дозволяє легко міркувати про порядок, у якому все відбувається.

Я не впевнений, як називати цей тип підходу. Я не бачив, щоб інші двигуни GUI робили це, і це мій власний екзотичний підхід до мого. Але перед тим, як я намагався реалізувати багатопотокові рамки графічного інтерфейсу за допомогою спостерігачів або черг подій, у мене виникли величезні труднощі з його налагодженням, а також наткнувся на деякі незрозумілі умови гонки та тупики, які я не був достатньо розумним, щоб виправити таким чином, щоб змусити мене почувати себе впевнено щодо рішення (деякі люди можуть це зробити, але я недостатньо розумний). Мій перший проект ітерації просто закликав слот безпосередньо через сигнал, і деякі слоти потім породили б інші потоки для роботи з асинхронізацією, і це було найважче міркувати, і я натрапив на умови гонки та тупики. Друга ітерація використовувала чергу подій, і це було трохи легше міркувати, але не досить легко, щоб мій мозок це зробив, все ще не стикаючись із незрозумілим тупиком та умовами перегонів. Третя і остання ітерація використовувала описаний вище підхід, і, нарешті, це дозволило мені створити багатопотоковий графічний інтерфейс GUI, який навіть тупий простак, як я, міг правильно реалізувати.

Тоді цей тип остаточного багатопотокового GUI-дизайну дозволив мені придумати щось інше, що було набагато простіше міркувати та уникати тих помилок, які я схиляв до помилок, і одна з причин, за якими я вважаю це набагато простіше міркувати на принаймні, через ці однорідні петлі і те, як вони ніби нагадували контрольний потік, подібний до того, коли я опитувався в дні DOS (хоча це насправді не опитування і виконання роботи лише тоді, коли треба виконати роботу). Ідея полягала в тому, щоб відійти якнайдалі від моделі обробки подій, що передбачає неоднорідні петлі, неоднорідні побічні ефекти, неоднорідні потоки управління, а також все більше працювати над однорідними петлями, що діють рівномірно на однорідних даних та ізолюють і об'єднання побічних ефектів таким чином, щоб було простіше зосередитись на "що"


1
"як, наприклад, використання змінних умов для сповіщення потоків, щоб прокинутися" Здається, що розташування на основі подій / спостерігача, а не опитування.
Джош Касвелл

Я вважаю різницю дуже тонкою, але сповіщення якраз у формі "Там треба виконати роботу", щоб прокинути теми. Наприклад, модель спостерігача може, змінюючи розмір батьківського контролю, каскадно змінювати розмір викликів вниз по ієрархії за допомогою динамічної диспетчеризації. У речей буде функція подій зміни розміру, опосередковано викликана негайно. Тоді вони можуть негайно перефарбуватися. Тоді, якщо ми використовуємо чергу подій, зміна батьківського елемента управління може потіснити події зміни розміру вниз по ієрархії, і тоді функції зміни розміру для кожного

... Точка може потім підштовхнути події перефарбовування, які також викликаються відкладеним способом після завершення зміни розміру, і все від потоку обробки центральної події. І я вважаю, що централізація корисна трохи принаймні, що стосується налагодження та можливості легко міркувати про те, де відбувається обробка (включаючи яку нитку) ... Тоді те, що я вважаю найближчим до опитування, не є ні одним із ці рішення ...

Наприклад, було б таке, LayoutSystemяке зазвичай спить, але коли користувач змінює розмір елемента керування, він використовує змінну умови для пробудження LayoutSystem. Потім LayoutSystemзмінює розмір усіх необхідних елементів керування і повертається до сну. У процесі прямокутних областей, у яких знаходяться віджети, позначаються як потрібні оновлення, після чого PaintSystemпрокидається і проходить через ці прямокутні області, перефарбовуючи ті, які потребують перемальовування у плоскій послідовній петлі.

Таким чином, сама змінна умова слідує шаблону спостерігача, щоб сповістити потоки, щоб прокинутися, але ми не передаємо будь-яку інформацію більше, ніж "треба виконати роботу". І кожна система, яка прокидається, присвячена обробці речей у дуже простому циклі із застосуванням дуже однорідного завдання, на відміну від черги подій, яка має неоднорідні завдання (вона може містити еклектичну суміш подій, що обробляються).

-4

Я даю вам більш детальний огляд концептуального способу мислення щодо спостерігача. Подумайте про такий сценарій, як підписка на канали YouTube. Існує кількість користувачів, які підписалися на канал, і як тільки буде оновлення на каналі, яке складається з багатьох відео, абонент отримує повідомлення про те, що в цьому конкретному каналі є зміни. Отже, ми дійшли висновку, що якщо канал є SUBJECT, який має можливість передплатити, скасувати підписку та повідомити всіх ОБОВ'ЯЗОК, які зареєстровані в каналі.


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