В чем разница между асинхронными и неблокирующими вызовами? Также между блокировкой и синхронными звонками (с примерами, пожалуйста)?
В чем разница между асинхронными и неблокирующими вызовами? Также между блокировкой и синхронными звонками (с примерами, пожалуйста)?
Ответы:
Во многих случаях это разные названия одной и той же вещи, но в некоторых контекстах они совершенно разные. Так что это зависит. Терминология не применяется полностью согласованным образом во всей индустрии программного обеспечения.
Например, в классическом API сокетов неблокирующим сокетом является тот, который просто немедленно возвращается со специальным сообщением об ошибке «будет блокировать», тогда как блокирующий сокет был бы заблокирован. Вы должны использовать отдельную функцию, например select
или, poll
чтобы узнать, когда подходящее время для повторной попытки.
Но асинхронные сокеты (поддерживаемые сокетами Windows) или шаблон асинхронного ввода-вывода, используемый в .NET, более удобны. Вы вызываете метод для запуска операции, и фреймворк перезванивает вам, когда это будет сделано. Даже здесь есть принципиальные различия. Асинхронные сокеты Win32 «маршалируют» свои результаты в конкретный поток GUI, передавая сообщения Window, тогда как асинхронный ввод-вывод .NET является свободным потоком (вы не знаете, из какого потока будет вызываться ваш обратный вызов).
Так что они не всегда означают одно и то же. Чтобы получить пример сокета, мы могли бы сказать:
синхронный / асинхронный - для описания отношений между двумя модулями.
блокирование / неблокирование - это описание ситуации одного модуля.
Пример:
Модуль 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, четырьмя словами.
Более важные вещи: когда мы используем синхронный вместо асинхронного? когда мы используем блокировку вместо неблокирования?
Почему Nginx неблокируемый? Почему Apache блокирует?
Чтобы сделать правильный выбор, вы должны проанализировать свои потребности и проверить производительность различных архитектур. Нет такой архитектуры, которая бы подходила для различных нужд.
Если поставить этот вопрос в контексте 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) {...}
}
}
FileChannel
не выбирается и не может быть настроен на неблокирование.
Как вы, вероятно, можете видеть из множества разных (и часто взаимоисключающих) ответов, это зависит от того, кого вы спрашиваете. В некоторых областях термины являются синонимами. Или каждый из них может ссылаться на два сходных понятия:
В любом случае цель состоит в том, чтобы позволить программе не блокироваться в ожидании завершения медленного процесса - единственное реальное отличие состоит в том, как программа будет реагировать. Какой термин относится к тому, который также изменяется от программиста к программисту, от языка к языку или от платформы к платформе. Или термины могут относиться к совершенно другим понятиям (таким как использование синхронного / асинхронного по отношению к программированию потоков).
Извините, но я не верю, что есть хоть один правильный ответ, который является глобально верным.
А неблокируемом вызов возвращается сразу с любыми данными доступны: полное число байтов не требуется, меньше, или вообще.
Асинхронный вызов запрашивает передачу , которая будет выполняться во всей своей полноте () , но будет завершена в какой - то момент в будущем.
Неблокирующая: эта функция не будет ждать в стеке.
Асинхронный: работа может продолжаться от имени вызова функции после того, как этот вызов покинул стек
Синхронный определяется как происходящий одновременно.
Асинхронный определяется как не происходящий одновременно.
Это то, что вызывает первое замешательство. Синхронный на самом деле то, что известно как параллельный. Пока асинхронный является последовательным, сделайте это, затем сделайте это.
Теперь вся проблема заключается в моделировании асинхронного поведения, потому что у вас есть какая-то операция, для которой требуется ответ другой, прежде чем она может начаться. Таким образом, это проблема координации, как вы узнаете, что теперь можете начать эту операцию?
Самое простое решение известно как блокировка.
Блокировка - это когда вы просто решаете подождать, пока другая вещь будет выполнена, и вернете вам ответ, прежде чем переходить к операции, которая в нем нуждалась.
Так что, если вам нужно положить масло на тост, и, таким образом, вам сначала нужно поджарить разводимого. Вы могли бы скоординировать их так, чтобы сначала вы жарили их, а потом бесконечно смотрели на тостер, пока он не поднимется, а затем вы продолжите наливать на них масло.
Это самое простое решение, и оно работает очень хорошо. Нет никакой реальной причины не использовать его, если у вас нет других вещей, которые вам нужно делать, которые не требуют координации с операциями. Например, мыть посуду. Зачем ждать, уставившись на тостер, чтобы тост выскочил, если вы знаете, что это займет немного времени, и вы можете помыть целое блюдо, пока оно заканчивается?
Вот где в игру вступают два других решения, известных как неблокирующие и асинхронные.
Неблокирование - это когда вы решаете делать другие несвязанные вещи, ожидая выполнения операции. Проверка доступности ответа по вашему усмотрению.
Так что вместо того, чтобы смотреть на тостер, чтобы он лопнул. Вы идете и моете целое блюдо. И затем вы смотрите на тостер, чтобы увидеть, если тосты лопнули. Если они этого не делают, вы идете мыть другое блюдо, проверяя тостер между каждым блюдом. Когда вы видите, что тосты лопнули, вы перестаете мыть посуду, а вместо этого вы берете тост и переходите к тому, чтобы положить на него масло.
Тем не менее, необходимость постоянно проверять тосты может раздражать, представьте, что тостер находится в другой комнате. В перерывах между блюдами вы тратите свое время на посещение этой другой комнаты, чтобы проверить тост.
Здесь идет асинхронный.
Асинхронный - это когда вы решаете делать другие несвязанные вещи, ожидая, когда будет выполнена операция. Однако вместо того, чтобы проверять его, вы делегируете работу по проверке чему-то другому, это может быть сама операция или наблюдатель, и вы должны уведомить об этом и, возможно, прервать вас, когда ответ будет доступен, чтобы вы могли перейти к другой операции, которая нужно было
Это странная терминология. Не имеет большого смысла, поскольку все эти решения являются способами создания асинхронной координации зависимых задач. Вот почему я предпочитаю называть его вечерним.
Так что для этого вы решили обновить тостер, чтобы он подал звуковой сигнал, когда тосты будут сделаны. Вы постоянно слушаете, даже когда вы моете посуду. Услышав звуковой сигнал, вы встаете в очередь в своей памяти, что, как только вы закончите мыть блюдо, вы остановитесь и положите масло на тост. Или вы можете прервать мытье текущего блюда и сразу же разбираться с тостами.
Если у вас возникли проблемы со звуком, вы можете попросить своего партнера посмотреть тостер и сказать, когда тост будет готов. Ваш партнер может сам выбрать любую из трех вышеуказанных стратегий, чтобы координировать свою задачу: наблюдать за тостером и сообщать вам, когда он будет готов.
В заключение отметим, что хорошо понимать, что хотя неблокирование и асинхронность (или то, что я предпочитаю называть вечерними) действительно позволяют вам делать другие вещи, пока вы ждете, у вас их тоже нет. Вы можете постоянно проверять состояние неблокирующего вызова, ничего не делая. Хотя зачастую это хуже, чем блокирование (например, глядя на тостер, затем в сторону, затем возвращайтесь к нему, пока оно не будет сделано), поэтому многие неблокирующие API-интерфейсы позволяют вам перейти из режима блокировки в него. Для Evented, вы можете просто подождать, пока вы не получите уведомление. Недостатком в этом случае является то, что добавление уведомления было сложным и потенциально дорогостоящим с самого начала. Вы должны были купить новый тостер с функцией звукового сигнала или убедить своего партнера следить за ним.
И еще одна вещь, вам нужно понять компромиссы, которые обеспечивают все три. Одно явно не лучше, чем другие. Подумай о моем примере. Если ваш тостер очень быстрый, у вас не будет времени помыть посуду, даже не начать мыть ее, вот как быстро ваш тостер. Начать с чего-то другого в этом случае - просто трата времени и усилий. Блокировка будет делать. Точно так же, если мытье посуды займет в 10 раз больше времени, чем поджаривание. Вы должны спросить себя, что важнее сделать? К тому времени тост может стать холодным и твердым, не стоит, блокировка тоже подойдет. Или вы должны выбрать более быстрые вещи, пока вы ждете. Там более очевидно, но мой ответ уже довольно длинный, моя точка зрения заключается в том, что вам нужно подумать обо всем этом, а также о сложностях реализации каждого из них, чтобы решить, стоит ли это того, и стоит ли это ».
Редактировать:
Хотя это уже долго, я также хочу, чтобы это было завершено, поэтому я добавлю еще два пункта.
1) Также обычно существует четвертая модель, известная как мультиплексная . Это когда вы ожидаете одну задачу, вы запускаете другую, и пока вы ждете обе, вы запускаете еще одну и т. Д., Пока у вас не будет запущено много задач, а затем вы ожидаете простоя, но на всех их. Поэтому, как только это будет сделано, вы можете приступить к обработке его ответа, а затем вернуться к ожиданию остальных. Он называется мультиплексным, потому что пока вы ждете, вам нужно проверять каждую задачу одну за другой, чтобы убедиться, что они выполнены, до тех пор, пока одна из них не будет выполнена. Это что-то вроде расширения поверх обычной неблокировки.
В нашем примере это было бы похоже на запуск тостера, затем посудомоечной машины, микроволновой печи и т. Д., А затем ожидание любого из них. Где бы вы ни проверили тостер, чтобы убедиться, что он готов, если нет, вы бы проверили посудомоечную машину, если нет, микроволновую печь и еще раз.
2) Несмотря на то, что я считаю, что это большая ошибка, синхронное часто используется для обозначения одной вещи за раз. И асинхронный много вещей одновременно. Таким образом, вы увидите синхронную блокировку и неблокирование, используемые для обозначения блокирования и неблокирования. И асинхронная блокировка, и неблокирование используются для обозначения мультиплексирования и выравнивания.
Я не очень понимаю, как мы туда попали. Но когда дело доходит до ввода-вывода и вычислений, синхронные и асинхронные часто относятся к тому, что более известно как неперекрывающиеся и перекрывающиеся. То есть асинхронный означает, что ввод-вывод и вычисления накладываются друг на друга, то есть происходят одновременно. В то время как синхронно означает, что это не так, это происходит последовательно. Для синхронной неблокировки это означает, что вы не запускаете другие операции ввода-вывода или вычисления, а просто заняты ожиданием и имитацией блокирующего вызова. Я бы хотел, чтобы люди перестали злоупотреблять синхронным и асинхронным. Так что я не поощряю это.
Блокировка вызова: управление возвращается только после завершения вызова.
Неблокирующий вызов: управление возвращается немедленно. Позже ОС как-то уведомляет процесс о том, что вызов завершен.
Синхронная программа: программа, которая использует блокировку вызовов. Чтобы не зависать во время вызова, он должен иметь 2 или более потоков (поэтому он называется синхронным - потоки работают синхронно).
Асинхронная программа: программа, которая использует неблокирующие вызовы. Он может иметь только 1 поток и при этом оставаться интерактивным.
Они отличаются только правописанием. Там нет разницы в том, что они ссылаются. Чтобы быть техническими, можно сказать, что они отличаются по акценту. Неблокируемый относится к потоку управления (он не блокирует.) Асинхронный относится к тому, когда обрабатывается событие \ data (не синхронно)
Модели блокировки требуют, чтобы инициирующее приложение блокировало при запуске ввода-вывода. Это означает, что невозможно перекрывать обработку и ввод-вывод одновременно. Синхронная неблокирующая модель допускает перекрытие обработки и ввода-вывода, но требует, чтобы приложение проверяло состояние ввода-вывода на регулярной основе. Это оставляет асинхронный неблокирующий ввод / вывод, который допускает перекрытие обработки и ввода / вывода, включая уведомление о завершении ввода / вывода.
Блокировка: управление возвращается к вызову прецесса после завершения обработки примитива (синхронного или асинхронного)
Неблокирующее: управление возвращается к процессу сразу после вызова