В чем разница между: асинхронной, неблокирующей и событийной архитектурой?


86
  1. Какая разница между:

    • Асинхронный ,
    • Неблокирующий и
    • Архитектуры на базе событий ?
  2. Может ли что-то быть как асинхронным, так и неблокирующимоснованным на событиях )?

  3. Что важнее всего в программировании, чтобы что-то было: асинхронное, неблокирующее и / или событийная (или все 3)?

Если бы вы могли привести примеры, это было бы здорово.

Этот вопрос задается, потому что я читал эту замечательную статью на StackOverflow по аналогичной теме, но она не отвечает на мои вопросы выше.

Ответы:


91

Асинхронный Асинхронный буквально означает несинхронный. Электронная почта асинхронная. Вы отправляете письмо и не ожидаете ответа СЕЙЧАС. Но это не неблокирующий. По сути, это означает архитектуру, в которой «компоненты» отправляют сообщения друг другу, не ожидая немедленного ответа. HTTP-запросы синхронны. Отправьте заявку и получите ответ.

Неблокирование Этот термин чаще всего используется с вводом-выводом. Это означает, что когда вы выполняете системный вызов, он немедленно возвращается с любым результатом, не переводя ваш поток в спящий режим (с большой вероятностью). Например, неблокирующие вызовы чтения / записи возвращаются со всем, что они могут сделать, и ожидают, что вызывающий абонент выполнит вызов снова. Например, try_lock - это неблокирующий вызов. Он будет заблокирован, только если блокировка может быть получена. Обычная семантика для системных вызовов - блокировка. read будет ждать, пока не будут получены данные, и переведет вызывающий поток в спящий режим.

Event-база Этот термин происходит от libevent. неблокирующие вызовы чтения / записи сами по себе бесполезны, потому что они не говорят вам, «когда» вы должны перезвонить им (повторить попытку). select / epoll / IOCompletionPort и т. д. - это разные механизмы для определения из ОС, «когда» эти вызовы должны возвращать «интересные» данные. Libevent и другие подобные библиотеки предоставляют оболочки для этих средств мониторинга событий, предоставляемых различными ОС, и предоставляют согласованный API для работы, работающий в разных операционных системах. Неблокирующий ввод-вывод идет рука об руку с Event-base.

Я думаю, что эти термины частично совпадают. Например, протокол HTTP является синхронным, но реализация HTTP с использованием неблокирующего ввода-вывода может быть асинхронной. Опять же, неблокирующий вызов API, такой как чтение / запись / try_lock, является синхронным (он немедленно дает ответ), но «обработка данных» является асинхронной.


2
Хороший момент о неблокировании, требующем постоянного опроса, в то время как асинхронный режим может быть основан на push.
Александр Торстлинг,

Вы определили синхронный как получение немедленного ответа, но когда я гуглию синхронно, все словари определяют его как «происходит одновременно», а не «немедленный ответ».
IntelliData,

4
Как меня блокируют, когда я отправляю электронное письмо, но не жду ответа? Я могу заняться своими делами, ожидая ответа.
Корай Тугай

20

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

Рассмотрим последовательный порт, из которого код захочет получить 1000 байт.

В архитектуре блокирующего чтения код будет ждать, пока не прибудет 1000 байт, или пока он не решит отказаться.

В архитектуре с асинхронным чтением код сообщит драйверу, что он хочет 1000 байтов, и получит уведомление, когда будет получено 1000 байтов.

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

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


5

Итак, чтобы ответить на ваш первый и второй вопрос:

Неблокирование фактически то же самое, что и асинхронный - вы делаете вызов, и вы получите результат позже, но пока это происходит, вы можете делать что-то еще. Блокировка - наоборот. Вы ждете звонка, прежде чем продолжить свое путешествие.

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

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

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

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

Если вы пишете внутренний веб-сервер, действуйте осторожно. Помните, что вы можете масштабировать по горизонтали гораздо дешевле. Netflix / Amazon / Google / Facebook - очевидные исключения из этого правила просто потому, что для них дешевле использовать меньше оборудования.

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

1) Это превращается в отказ в обслуживании продуктивности ... вам нужно гораздо больше думать, и вы делаете много ошибок на этом пути.

2) Следы стека в реактивном коде становятся неразборчивыми - трудно понять, что называется, что, когда, почему и как. Удачи с отладкой.

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

4) Сложнее проверить.

5) Сложнее поддерживать.

6) Это больно. Программирование должно доставлять удовольствие и веселье. Только мазохисты любят боль. Люди, которые пишут параллельные / реактивные фреймворки, - садисты.

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

  1. Да, код может быть асинхронным, неблокирующим И основанным на событиях.

  2. Самое важное в программировании - убедиться, что ваш код работает и реагирует в приемлемое время. Придерживайтесь этого ключевого принципа, и вы не ошибетесь.


** ОБНОВЛЕНИЕ ** Поиграв с Go и изучив каналы и подпрограммы, я должен сказать, что мне на самом деле нравится делать мой код более параллельным, потому что конструкции языка берут на себя всю боль от авторов садистских фреймворков. У нас есть "безопасное слово" в мире асинхронной обработки - "Вперед!"
user924272

4

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

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

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

Концепция события - это нечто иное, что, грубо говоря, означает, что вы получаете информацию, когда что-то происходит. Если запись была выполнена асинхронно, может возникнуть событие, чтобы проинформировать другие части системы, как только запись будет выполнена. Остальные части будут реагировать на событие. Система может быть построена исключительно на событиях как на единственном способе связи между компонентами (подумайте о модели акторов), но это не обязательно.

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


2

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

Архитектура, основанная на событиях, основана на концепции кода, выполняемого в ответ на запускаемые события. Время выполнения кода обычно не детерминировано, но события могут вызывать методы блокировки; Тот факт, что система основана на событиях, не означает, что все, что она делает, не блокирует.

Как правило, асинхронная архитектура - это неблокирующая архитектура, основанная на событиях.

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

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

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


Разве ваш последний абзац не противоречит вашему заявлению о том, что «асинхронная архитектура ... неблокирующая»
nickb

Думаю, я не очень хорошо постарался ответить на часть вашего вопроса «определения»; Я выложу обновление. Но нет, природа однопоточного выполнения заключается в том, что каждая операция по своей сути блокирует во время выполнения , что делает асинхронность еще более полезной.
Мэтт Миллс
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.