асинхронный против неблокирующих


373

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



1
Я получил хорошее представление о различиях, читая книгу «Сетевое программирование Unix», Кол. 1, Глава 6.
Бин,

2
Интересная статья: Повышение производительности приложений с помощью асинхронного ввода-вывода . Он разделяет парадигмы ввода / вывода на 4 категории: (1) блокирующий + синхронный, (2) неблокирующий + синхронный, (3) блокирующий + асинхронный и (4) неблокирующий + асинхронный.
MS Dousti

@MSDousti Мне сказали, что это неправильно в некотором смысле, от эксперта Google.
Рик

@MSDousti После некоторого изучения, я думаю, что нет сочетаний (3) и (2), как вы описываете в комментариях. Проверьте определение Asynchronous, оно говорит о том же о неблокировании. Как вы видите верхний ответ, это подтверждает мое мнение. Функция опроса и обратного вызова - это просто способы / шаблоны для реализации асинхронного. Да, я говорю, что блокирующий, синхронный и неблокирующий, асинхронный - это две пары синонимов.
Рик

Ответы:


305

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

Например, в классическом API сокетов неблокирующим сокетом является тот, который просто немедленно возвращается со специальным сообщением об ошибке «будет блокировать», тогда как блокирующий сокет был бы заблокирован. Вы должны использовать отдельную функцию, например selectили, pollчтобы узнать, когда подходящее время для повторной попытки.

Но асинхронные сокеты (поддерживаемые сокетами Windows) или шаблон асинхронного ввода-вывода, используемый в .NET, более удобны. Вы вызываете метод для запуска операции, и фреймворк перезванивает вам, когда это будет сделано. Даже здесь есть принципиальные различия. Асинхронные сокеты Win32 «маршалируют» свои результаты в конкретный поток GUI, передавая сообщения Window, тогда как асинхронный ввод-вывод .NET является свободным потоком (вы не знаете, из какого потока будет вызываться ваш обратный вызов).

Так что они не всегда означают одно и то же. Чтобы получить пример сокета, мы могли бы сказать:

  • Блокирование и синхронность означают одно и то же: вы вызываете API, он останавливает поток, пока не получит какой-то ответ, и не вернет его вам.
  • Неблокирующая означает, что если ответ не может быть быстро возвращен, API немедленно возвращается с ошибкой и больше ничего не делает. Таким образом, должен существовать некоторый связанный способ запроса, готов ли API к вызову (то есть эффективная имитация ожидания, чтобы избежать ручного опроса в узком цикле).
  • Асинхронный означает, что API всегда возвращается немедленно, начав «фоновую» попытку выполнить ваш запрос, поэтому должен быть какой-то связанный способ получения результата.

состояние готовности IO, а не состояние завершения IO; на Linux см. libaio
будет

4
Спасибо за то, что указали, что термины являются контекстно-зависимыми и иногда могут использоваться непоследовательно. Я нахожу в технологии особенно, но и в других областях, что часто более полезно признать этот факт, чем вступать в дебаты о том, какое точное определение является правильным, как это иногда бывает.
Чад NB

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

2
@ChadNB - как термины, неблокирование тесно связано с опросом. Относительно вопроса о том, «запоминает» ли API вашу попытку вызвать его: единственная причина, по которой API должен помнить, - это перезвонить вам. Если вы собираетесь вызывать его для повторного опроса, то вы уже должны поддерживать необходимое состояние, чтобы знать, чтобы выполнить этот последующий вызов, поэтому API не добавит никакой ценности, также поддерживая состояние.
Даниэль Уорвикер

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

56

синхронный / асинхронный - для описания отношений между двумя модулями.
блокирование / неблокирование - это описание ситуации одного модуля.

Пример:
Модуль X: «Я».
Модуль Y: «книжный магазин».
X спрашивает Y: есть ли у вас книга под названием "c ++ primer"?

1) блокировка: прежде чем Y ответит X, X продолжает ждать ответа. Теперь X (один модуль) блокируется. X и Y два потока или два процесса или один поток или один процесс? мы не знаем

2) неблокирующая: прежде чем Y ответит на X, X просто уходит туда и делает другие вещи. X может возвращаться каждые две минуты, чтобы проверить, закончил ли Y свою работу? Или X не вернется, пока Y не позвонит ему? Мы не знаем Мы только знаем, что X может делать другие вещи, прежде чем Y закончит свою работу. Здесь X (один модуль) является неблокирующим. X и Y два потока или два процесса или один процесс? мы не знаем НО мы уверены, что X и Y не могут быть одной нитью.

3) синхронно: прежде чем Y ответит X, X продолжает ждать ответа. Это означает, что X не может продолжать, пока Y не закончит свою работу. Теперь мы говорим: X и Y (два модуля) являются синхронными. X и Y два потока или два процесса или один поток или один процесс? мы не знаем

4) асинхронный: прежде чем Y ответит X, X уйдет туда и X сможет выполнять другие работы. X не вернется, пока Y не позвонит ему. Теперь мы говорим: X и Y (два модуля) являются асинхронными. X и Y два потока или два процесса или один процесс? мы не знаем НО мы уверены, что X и Y не могут быть одной нитью.


Пожалуйста, обратите внимание на два жирных предложения выше. Почему жирное предложение в 2) содержит два случая, тогда как жирное предложение в 4) содержит только один случай? Это ключ к разнице между неблокирующим и асинхронным.

Вот типичный пример неблокирования и синхронности:

// thread X
while (true)
{
    msg = recv(Y, NON_BLOCKING_FLAG);
    if (msg is not empty)
    {
        break;
    }
    sleep(2000); // 2 sec
}

// thread Y
// prepare the book for X
send(X, book);

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

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

Например, мы можем спроектировать такую ​​архитектуру:

// Module X = Module X1 + Module X2
// Module X1
while (true)
{
    msg = recv(many_other_modules, NON_BLOCKING_FLAG);
    if (msg is not null)
    {
        if (msg == "done")
        {
            break;
        }
        // create a thread to process msg
    }
    sleep(2000); // 2 sec
}
// Module X2
broadcast("I got the book from Y");


// Module Y
// prepare the book for X
send(X, book);

В приведенном здесь примере мы можем сказать, что

  • X1 неблокирует
  • X1 и X2 являются синхронными
  • X и Y асинхронные

Если вам нужно, вы также можете описать эти темы, созданные в X1, четырьмя словами.

Более важные вещи: когда мы используем синхронный вместо асинхронного? когда мы используем блокировку вместо неблокирования?

Почему Nginx неблокируемый? Почему Apache блокирует?

Чтобы сделать правильный выбор, вы должны проанализировать свои потребности и проверить производительность различных архитектур. Нет такой архитектуры, которая бы подходила для различных нужд.


7
ИМО лучший ответ, поскольку он улавливает суть концепции: отношения между одним или двумя участниками.
Фабио

и в 1, и в 3 Y действует как ОГРАНИЧЕННЫЙ ресурс. Там больше нет Y, чтобы помочь X
UVM

46
  • Асинхронный относится к тому, что делается параллельно , скажем, это другой поток.
  • Неблокирование часто относится к опросу , то есть проверке, выполняется ли данное условие (сокет доступен для чтения, устройство имеет больше данных и т. Д.)

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

Ну да, мы можем спорить о точной формулировке весь день :)
Николай Фетисов

но как вы объясните AIO в Linux? который использовал и асинхронный и неблокирующий. AIO LINKS
Djvu

16
Всем, кто читает этот ответ: это не спор о точной формулировке. Точно так же, как параллелизм и параллелизм не являются одинаковыми понятиями, и их различие не является вопросом формулировки. Асинхронность и параллелизм - два разных зверя, и этот ответ неточно делает их одинаковыми.
Ptival

2
Асинхронизация не обязательно означает, что она выполняется параллельно, см. Этот великолепный пост о стекопереработке о параллельном и параллельном программировании
БАРЖ

17

Если поставить этот вопрос в контексте NIO и NIO.2 в Java 7, асинхронный ввод-вывод является на один шаг более продвинутым, чем неблокирующая. С неблокирующими вызовами Java NIO можно было бы установить все каналы (SocketChannel, ServerSocketChannel, FileChannel и т. Д.) Как таковые путем вызова AbstractSelectableChannel.configureBlocking(false). Однако после возврата этих вызовов ввода-вывода вам, вероятно, все еще нужно будет контролировать проверки, такие как повторное чтение и запись и т. Д.
Например,

while (!isDataEnough()) {
    socketchannel.read(inputBuffer);
    // do something else and then read again
}

С помощью асинхронного API в Java 7, эти элементы управления могут быть сделаны более универсальными способами. Одним из 2 способов является использование CompletionHandler. Обратите внимание, что оба readвызова не являются блокирующими.

asyncsocket.read(inputBuffer, 60, TimeUnit.SECONDS /* 60 secs for timeout */, 
    new CompletionHandler<Integer, Object>() {
        public void completed(Integer result, Object attachment) {...}  
        public void failed(Throwable e, Object attachment) {...}
    }
}

3
FileChannelне выбирается и не может быть настроен на неблокирование.
Микаэллиу

15

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

  • Одно из объяснений состоит в том, что вызов будет делать что-то в фоновом режиме, по существу, без присмотра, чтобы позволить программе не задерживаться в течение длительного процесса, который ей не нужно контролировать. Воспроизведение аудио может быть примером - программа может вызывать функцию для воспроизведения (скажем) mp3, и с этого момента может переходить к другим вещам, оставляя это для ОС, чтобы управлять процессом рендеринга аудио на звуковом оборудовании. ,
  • Альтернативная интерпретация заключается в том, что вызов будет выполнять то, что программа должна будет отслеживать, но позволит большей части процесса происходить в фоновом режиме, только уведомляя программу в критических точках процесса. Например, асинхронный файловый ввод-вывод может быть примером - программа предоставляет буфер операционной системе для записи в файл, а ОС только уведомляет программу о завершении операции или возникновении ошибки.

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

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


1
+1 Хороший ответ. Люди должны знать , что «асинхронный» может означать либо неблокируемый, или асинхронную Microsoft ( на основе событий / обратный вызов) подход.
инженер

14

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

Асинхронный вызов запрашивает передачу , которая будет выполняться во всей своей полноте () , но будет завершена в какой - то момент в будущем.


неблокирование не дает никакого результата вообще
генеральный директор Apartico

9

Неблокирующая: эта функция не будет ждать в стеке.

Асинхронный: работа может продолжаться от имени вызова функции после того, как этот вызов покинул стек


1
@Marenz означает, что вы не можете делать неблокирование напрямую с помощью вызовов posix. Это не меняет значения, которое он дает здесь.
Tmc

@Marenz Это означает, что флаг игнорируется для файлов. Это не влияет на значение этого ответа.
Маркиз Лорн

8

Синхронный определяется как происходящий одновременно.

Асинхронный определяется как не происходящий одновременно.

Это то, что вызывает первое замешательство. Синхронный на самом деле то, что известно как параллельный. Пока асинхронный является последовательным, сделайте это, затем сделайте это.

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

Самое простое решение известно как блокировка.

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

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

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

Вот где в игру вступают два других решения, известных как неблокирующие и асинхронные.

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

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

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

Здесь идет асинхронный.

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

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

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

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

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

И еще одна вещь, вам нужно понять компромиссы, которые обеспечивают все три. Одно явно не лучше, чем другие. Подумай о моем примере. Если ваш тостер очень быстрый, у вас не будет времени помыть посуду, даже не начать мыть ее, вот как быстро ваш тостер. Начать с чего-то другого в этом случае - просто трата времени и усилий. Блокировка будет делать. Точно так же, если мытье посуды займет в 10 раз больше времени, чем поджаривание. Вы должны спросить себя, что важнее сделать? К тому времени тост может стать холодным и твердым, не стоит, блокировка тоже подойдет. Или вы должны выбрать более быстрые вещи, пока вы ждете. Там более очевидно, но мой ответ уже довольно длинный, моя точка зрения заключается в том, что вам нужно подумать обо всем этом, а также о сложностях реализации каждого из них, чтобы решить, стоит ли это того, и стоит ли это ».

Редактировать:

Хотя это уже долго, я также хочу, чтобы это было завершено, поэтому я добавлю еще два пункта.

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

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

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

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


Не уверен, почему вы сказали: «Синхронный определяется как происходящий одновременно».? Вся идея в том, что это не одновременно, иначе не происходит одновременно.
Хельсинг

Это была отличная аналогия! Вы просто поджарили это!
D-кодер

@Helsing Это буквально, что означает слово. Синхронный означает то же самое время, а асинхронный означает не то же самое время: с. Причина, по которой что-то является асинхронным, заключается в том, что это не может происходить одновременно, это должно происходить до или после. Если это может произойти одновременно, вы можете просто распараллелить это или сделать это в любом порядке, и вам не понадобится явная синхронизация. Вот почему асинхронное программирование - это то, что нужно делать, потом ждать этих вещей, а затем и т. Д. Потому что ни одна из этих вещей не может происходить одновременно.
Дидье А.

@Helsing Кроме того, одновременный не то же самое, что параллельный. Это не означает, что две вещи происходят одновременно, это означает только прогресс в более чем одной вещи, прежде чем любая из них завершится. Этого можно достичь с помощью распараллеливания или просто чередования, или переключения задач.
Дидье А.

5

Блокировка вызова: управление возвращается только после завершения вызова.

Неблокирующий вызов: управление возвращается немедленно. Позже ОС как-то уведомляет процесс о том, что вызов завершен.


Синхронная программа: программа, которая использует блокировку вызовов. Чтобы не зависать во время вызова, он должен иметь 2 или более потоков (поэтому он называется синхронным - потоки работают синхронно).

Асинхронная программа: программа, которая использует неблокирующие вызовы. Он может иметь только 1 поток и при этом оставаться интерактивным.


1
Неблокирующий вызов: управление возвращается после выполнения столько, сколько может быть сделано практически немедленно; метод показывает, сколько было сделано. Это отличается от асинхронного вызова, который ведет себя так, как вы описали для блокировки вызова.
суперкат

0

Они отличаются только правописанием. Там нет разницы в том, что они ссылаются. Чтобы быть техническими, можно сказать, что они отличаются по акценту. Неблокируемый относится к потоку управления (он не блокирует.) Асинхронный относится к тому, когда обрабатывается событие \ data (не синхронно)


0

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


-2

Блокировка: управление возвращается к вызову прецесса после завершения обработки примитива (синхронного или асинхронного)

Неблокирующее: управление возвращается к процессу сразу после вызова


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