Я люблю опитування! Я? Так! Я? Так! Я? Так! Я ще? Так! А зараз? Так!
Як вже згадували інші, це може бути неймовірно неефективно, якщо ви опитуєтесь лише для того, щоб повернути той самий незмінний стан знову і знову. Такий рецепт для спалювання циклів процесора та значно скорочення часу автономної роботи на мобільних пристроях. Звичайно, це не марно, якщо ви щоразу повертаєте нове і осмислене стан зі швидкістю, не швидшою від бажаної.
Але головна причина, що я люблю опитування, - через її простоту та передбачуваний характер. Ви можете простежити код і легко побачити, коли і де відбуватимуться справи, і в якій нитці. Якщо б теоретично ми жили у світі, де опитування було незначним відходом (хоч і реальність далека від нього), то я вважаю, що це спростить підтримання коду величезною угодою. І в цьому користь опитування та витягування, як я бачу, якби ми могли нехтувати ефективністю, навіть якщо в цьому випадку ми не повинні.
Коли я почав програмувати в епоху 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 (хоча це насправді не опитування і виконання роботи лише тоді, коли треба виконати роботу). Ідея полягала в тому, щоб відійти якнайдалі від моделі обробки подій, що передбачає неоднорідні петлі, неоднорідні побічні ефекти, неоднорідні потоки управління, а також все більше працювати над однорідними петлями, що діють рівномірно на однорідних даних та ізолюють і об'єднання побічних ефектів таким чином, щоб було простіше зосередитись на "що"