В чем разница между асинхронными и неблокирующими вызовами? Также между блокировкой и синхронным вызовом (пожалуйста, с примерами)?
асинхронные и неблокирующие вызовы? также между блокировкой и синхронным
Ответы (14)
Во многих случаях это разные названия одного и того же предмета, но в некоторых контекстах они совершенно разные. Так что это зависит от обстоятельств. Терминология не применяется единообразно во всей индустрии программного обеспечения.
Например, в классическом API сокетов неблокирующий сокет - это тот, который просто немедленно возвращается со специальным сообщением об ошибке «заблокирует», тогда как блокирующий сокет будет заблокирован. Вы должны использовать отдельную функцию, такую как select
или poll
, чтобы узнать, когда лучше всего повторить попытку.
Но асинхронные сокеты (поддерживаемые сокетами Windows) или асинхронный шаблон ввода-вывода, используемый в .NET, более удобны. Вы вызываете метод, чтобы начать операцию, и фреймворк перезвонит вам, когда это будет сделано. Даже здесь есть принципиальные отличия. Асинхронные сокеты Win32 «маршалируют» свои результаты в конкретный поток графического интерфейса, передавая сообщения Window, тогда как асинхронный ввод-вывод .NET является свободным потоком (вы не знаете, в каком потоке будет вызван ваш обратный вызов).
Так что они не всегда означают одно и то же. Чтобы выделить пример сокета, мы могли бы сказать:
- Блокировка и синхронность означают одно и то же: вы вызываете API, он вешает поток, пока не получит какой-то ответ, и вернет его вам.
- Неблокирование означает, что если ответ не может быть возвращен быстро, API немедленно возвращается с ошибкой и больше ничего не делает. Таким образом, должен быть какой-то связанный способ запросить, готов ли API к вызову (то есть, чтобы имитировать ожидание эффективным способом, чтобы избежать ручного опроса в тесном цикле).
- Асинхронный означает, что API всегда немедленно возвращается, начав «фоновые» усилия для выполнения вашего запроса, поэтому должен быть какой-то связанный способ получения результата.
синхронный / асинхронный - это описание отношений между двумя модулями.
блокировка / неблокирование - описание ситуации с одним модулем.
Пример:
Модуль X: I.
Модуль Y: книжный магазин.
X спрашивает Y: у вас есть книга с названием C ++ primer?
блокировка: до того, как Y ответит X, X продолжает ждать ответа. Теперь X (один модуль) блокируется. X и Y - это два потока или два процесса, или один поток, или один процесс? мы НЕ ЗНАЕМ.
неблокирующий: прежде чем Y ответит X, X просто уйдет оттуда и займется другими делами. X может возвращаться каждые две минуты, чтобы проверить, завершил ли Y свою работу? Или X не вернется, пока Y не позвонит ему? Мы не знаем. Мы знаем только, что X может делать другие вещи до того, как Y закончит свою работу. Здесь X (один модуль) неблокирующий. X и Y - это два потока или два процесса или один процесс? мы НЕ знаем. НО мы уверены, что X и Y не могут быть одним потоком.
синхронно: до того, как Y ответит X, X продолжает ждать ответа. Это означает, что X не может продолжать работу, пока Y не закончит свою работу. Теперь мы говорим: X и Y (два модуля) синхронны. X и Y - это два потока или два процесса, один поток или один процесс? мы НЕ ЗНАЕМ.
асинхронный: до того, как 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;
}
else
{
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
}
else
{
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, четырьмя словами.
Более важные вещи: когда мы используем синхронный вместо асинхронного? когда мы используем блокировку вместо неблокирования? Лучше ли блокировать X1, чем неблокировать? Делать X и Y синхронными лучше, чем асинхронными? Почему Nginx не блокирует? Почему блокируется Apache? Вы должны ответить на эти вопросы.
Чтобы сделать правильный выбор, вы должны проанализировать свои потребности и протестировать производительность различных архитектур. Нет такой архитектуры, которая подходила бы для различных нужд.
- Асинхронный относится к чему-то, что выполняется параллельно, например, к другому потоку.
- Неблокирование часто относится к опросу, то есть проверке выполнения данного условия (сокет доступен для чтения, на устройстве есть больше данных и т. д.)
Синхронный определяется как происходящее в одно и то же время.
Асинхронный определяется как то, что не происходит одновременно.
Вот что вызывает первое недоумение. На самом деле синхронный - это то, что называется параллельным. Пока асинхронный режим является последовательным, сделайте это, а затем сделайте то.
Теперь вся проблема заключается в моделировании асинхронного поведения, потому что у вас есть операция, которая требует ответа от другой, прежде чем она может начаться. Таким образом, это проблема координации, как вы узнаете, что теперь можете начать эту операцию?
Самое простое решение - блокировка.
Блокировка - это когда вы просто выбираете дождаться выполнения других действий и возвращаете вам ответ, прежде чем переходить к операции, для которой он был необходим.
Так что, если вам нужно полить тосты маслом, и при этом сначала нужно поджарить поросли. То, как вы их координируете, заключается в том, что вы сначала поджариваете выращенное, затем бесконечно смотрите на тостер, пока он не лопнет тост, а затем вы продолжаете поливать их маслом.
Это самое простое решение, и оно работает очень хорошо. Нет реальной причины не использовать его, если только у вас нет других дел, которые вам нужно делать, которые не требуют координации с операциями. Например, мыть посуду. Зачем ждать, постоянно глядя на тостер, пока тост не лопнет, если вы знаете, что это займет немного времени, и вы можете вымыть все блюдо, пока оно закончится?
Здесь вступают в игру два других решения, известных соответственно как неблокирующее и асинхронное.
Неблокирование - это когда вы решаете заняться другими несвязанными действиями, ожидая завершения операции. Проверяем наличие ответа, как сочтете нужным.
Так что вместо того, чтобы смотреть на тостер, чтобы он лопнул. Вы идете и моете всю посуду. Затем вы смотрите на тостер, чтобы увидеть, не появились ли тосты. Если они этого не сделали, вы идете мыть еще одну посуду, проверяя тостер между каждым блюдом. Когда вы видите, что тосты лопнули, вы прекращаете мыть посуду, а вместо этого берете тост и продолжаете намазывать их маслом.
Однако необходимость постоянно проверять тосты может раздражать, представьте, что тостер находится в другой комнате. Между блюдами вы тратите время, идя в другую комнату, чтобы проверить тосты.
А вот и асинхронность.
Асинхронный - это когда вы решаете выполнять другие несвязанные действия, ожидая завершения операции. Однако вместо того, чтобы проверять его, вы делегируете работу по проверке чему-то другому, может быть самой операцией или наблюдателем, и у вас есть эта вещь, которая уведомляет и, возможно, прерывает вас, когда ответ доступен, чтобы вы могли перейти к другой операции, которая нуждался в этом.
Странная терминология. В этом нет большого смысла, поскольку все эти решения являются способами создания асинхронной координации зависимых задач. Вот почему я предпочитаю называть это вечерним.
Итак, для этого вы решили обновить свой тостер, чтобы он подал звуковой сигнал, когда тосты готовы. Вы постоянно слушаете, даже когда моете посуду. Услышав звуковой сигнал, вы вспоминаете о том, что как только вы закончите мыть текущую посуду, вы остановитесь и положите масло на тосты. Или вы можете прервать мытье текущего блюда и сразу приготовить тосты.
Если вы не слышите звуковой сигнал, попросите партнера присмотреть за тостером и сказать вам, когда тост будет готов. Ваш партнер может сам выбрать любую из трех вышеперечисленных стратегий, чтобы скоординировать свою задачу по наблюдению за тостером и сообщению вам, когда они будут готовы.
И последнее замечание: хорошо понимать, что, хотя неблокирование и асинхронность (или то, что я предпочитаю называть evented) действительно позволяют вам делать другие вещи, пока вы ждете, у вас тоже нет. Вы можете выбрать постоянный цикл проверки состояния неблокирующего вызова, ничего не делая. Это часто хуже, чем блокировка (например, смотреть на тостер, затем в сторону, а затем снова на него, пока он не будет готов), поэтому многие неблокирующие API-интерфейсы позволяют вам перейти из него в режим блокировки. В случае события вы можете просто подождать, пока не получите уведомление. Обратной стороной этого случая является то, что добавление уведомления было сложным и потенциально дорогостоящим с самого начала. Вам нужно было купить новый тостер с функцией звукового сигнала или убедить вашего партнера посмотреть его вместо вас.
И еще одна вещь: вы должны понимать компромиссы, которые обеспечивают все три. Один явно не лучше других. Вспомни мой пример. Если ваш тостер такой быстрый, у вас не будет времени мыть посуду, даже если вы не начнете ее мыть, вот насколько быстр ваш тостер. В таком случае начинать что-то другое - пустая трата времени и усилий. Блокировка подойдет. Точно так же, если мытье посуды займет в 10 раз больше времени, чем поджаривание. Вы должны спросить себя, что для вас важнее? К тому времени тост может остыть и затвердеть, не стоит того, блокировка тоже подойдет. Или вам следует выбрать более быстрые дела, пока вы ждете. Есть более очевидные вещи, но мой ответ уже довольно длинный, я хочу сказать, что вам нужно подумать обо всем этом и о сложностях реализации каждого, чтобы решить, стоит ли оно того, и действительно ли это улучшит вашу пропускную способность или производительность.
Изменить:
Несмотря на то, что это уже большой объем, я также хочу, чтобы он был завершен, поэтому я добавлю еще два пункта.
- Также обычно существует четвертая модель, известная как мультиплексная. Это когда вы ждете одну задачу, вы запускаете другую, и пока вы ждете обеих, вы запускаете еще одну и так далее, пока у вас не будет запущено много задач, а затем вы ждете простоя, но на всех их. Итак, как только один из них будет выполнен, вы можете приступить к обработке его ответа, а затем вернуться к ожиданию остальных. Это называется мультиплексированием, потому что пока вы ждете, вам нужно проверять каждую задачу одну за другой, чтобы увидеть, выполнены ли они, ad vitam, пока одна из них не будет выполнена. Это своего рода расширение поверх обычной неблокирующей функции.
В нашем примере это похоже на запуск тостера, затем посудомоечной машины, затем микроволновой печи и т. Д. И затем ожидание любого из них. Если вы проверите тостер, чтобы убедиться, что он готов, если нет, вы можете проверить посудомоечную машину, если нет, микроволновую печь и еще раз.
- Несмотря на то, что я считаю это большой ошибкой, синхронность часто используется для обозначения чего-то одного. И асинхронно много вещей одновременно. Таким образом, вы увидите, что синхронная блокировка и неблокирование используются для обозначения блокировки и неблокирования. И асинхронная блокировка, и неблокирующая используются для обозначения мультиплексированного и четного.
Я действительно не понимаю, как мы туда попали. Но когда дело доходит до ввода-вывода и вычислений, синхронный и асинхронный часто относятся к тому, что более известно как неперекрывающееся и перекрывающееся. То есть асинхронность означает, что ввод-вывод и вычисления перекрываются, иначе говоря, происходят одновременно. Синхронность означает, что их нет, поэтому они происходят последовательно. Для синхронной неблокирующей обработки это будет означать, что вы не запускаете другие операции ввода-вывода или вычислений, вы просто заняты ожиданием и имитацией блокирующего вызова. Я хочу, чтобы люди перестали злоупотреблять синхронным и асинхронным. Так что я не поощряю это.
Помещая этот вопрос в контексте 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 эти элементы управления можно сделать более универсальными. Один из двух способов - использовать 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) {...}
}
}
FileChannel
нельзя выбрать и настроить на неблокирование.
- person michaelliu; 14.03.2014
Как вы, вероятно, можете видеть из множества различных (и часто взаимоисключающих) ответов, это зависит от того, кого вы спрашиваете. В некоторых случаях эти термины являются синонимами. Или каждый из них может относиться к двум похожим концепциям:
- Одна из интерпретаций состоит в том, что вызов будет делать что-то в фоновом режиме, по существу, без присмотра, чтобы программа не задерживалась длительным процессом, который ей не нужно контролировать. Примером может служить воспроизведение звука - программа может вызывать функцию для воспроизведения (скажем) mp3, и с этого момента может переходить к другим вещам, оставляя это ОС для управления процессом рендеринга звука на звуковом оборудовании. .
- Альтернативная интерпретация состоит в том, что вызов будет делать что-то, что программе необходимо будет отслеживать, но позволит большей части процесса происходить в фоновом режиме, уведомляя программу только в критических точках процесса. Например, асинхронный файловый ввод-вывод может быть примером - программа предоставляет буфер операционной системе для записи в файл, а ОС уведомляет программу только о завершении операции или возникновении ошибки.
В любом случае цель состоит в том, чтобы позволить программе не блокироваться в ожидании завершения медленного процесса - единственное реальное различие - это то, как программа, как ожидается, отреагирует. Какой термин относится к тому, что также меняется от программиста к программисту, от языка к языку или от платформы к платформе. Или термины могут относиться к совершенно другим концепциям (например, к использованию синхронного / асинхронного по отношению к программированию потоков).
Извините, но я не верю, что существует единственный правильный ответ, который был бы верен во всем мире.
Неблокирующий вызов немедленно возвращается с любыми доступными данными: полным количеством запрошенных байтов, меньшим количеством байтов или их отсутствием.
асинхронный вызов запрашивает перевод, который будет выполнен полностью (целиком), но завершится в будущем.
Блокирующий вызов: управление возвращается только после завершения вызова.
Неблокирующий вызов: управление немедленно возвращается. Позже ОС каким-то образом уведомляет процесс о завершении вызова.
Синхронная программа: программа, использующая блокирующие вызовы. Чтобы не зависать во время вызова, в нем должно быть 2 и более потоков (поэтому он называется синхронным - потоки работают синхронно).
Асинхронная программа: программа, в которой используются неблокирующие вызовы. Он может иметь только 1 поток и при этом оставаться интерактивным.
Неблокирование: эта функция не будет ждать, пока находится в стеке.
Асинхронный: работа может продолжаться от имени вызова функции после того, как этот вызов покинул стек.
Синхронный означает запуск одного результата за другим, в последовательности.
Асинхронный означает совместное начало, не гарантируется последовательность результата
Блокировка означает что-то, что вызывает препятствие для выполнения следующего шага.
Неблокирование означает что-то, что продолжает работать, ничего не дожидаясь, преодолевая препятствие.
Пример блокировки: я стучу в дверь и жду, пока они ее откроют. (Я здесь без дела)
Неблокирующий, например: я стучу в дверь, если они открывают ее мгновенно, я приветствую их, захожу внутрь и т. д. Если они не открываются мгновенно, я иду в следующий дом и стучу в него . (Я делаю то или другое, а не без дела)
Синхронно, например: я выйду, только если пойдет дождь. (существует зависимость)
Асинхронный, например: я выйду. Может пойти дождь. (независимые события, не имеет значения, когда они происходят)
Синхронный или асинхронный, оба могут быть блокирующими или неблокирующими, и наоборот
Модели блокировки требуют, чтобы приложение-инициатор блокировалось при запуске ввода-вывода. Это означает, что невозможно одновременно перекрывать обработку и ввод-вывод. Синхронная неблокирующая модель допускает перекрытие обработки и ввода-вывода, но требует, чтобы приложение проверяло состояние ввода-вывода на регулярной основе. Это оставляет асинхронный неблокирующий ввод-вывод, который разрешает перекрытие обработки и ввода-вывода, включая уведомление о завершении ввода-вывода.
Проще говоря,
function sum(a,b){
return a+b;
}
является неблокирующим. в то время как асинхронный используется для выполнения задачи блокировки, а затем возврата ее ответа
Отличаются они только написанием. Нет разницы в том, к чему они относятся. Говоря техническим языком, можно сказать, что они различаются по акцентам. Неблокирование относится к потоку управления (он не блокируется). Асинхронный относится к тому, когда событие \ данные обрабатываются (не синхронно).
Блокировка: элемент управления возвращается к вызову прецессии после завершения обработки примитива (синхронизация или асинхронизация).
Неблокирующий: управление возвращается в действие сразу после вызова.