асинхронный против неблокирующих - PullRequest
337 голосов
/ 13 апреля 2010

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

Ответы [ 12 ]

275 голосов
/ 13 апреля 2010

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

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

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

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

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

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

Пример:
Модуль 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 блокирует?

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

15 голосов
/ 22 сентября 2012

Если поставить этот вопрос в контексте 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) {...}
    }
}
15 голосов
/ 13 апреля 2010

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

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

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

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

12 голосов
/ 27 февраля 2015

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

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

9 голосов
/ 13 апреля 2010

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

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

4 голосов
/ 15 мая 2018

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Edit:

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

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

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

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

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

2 голосов
/ 02 мая 2014

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

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


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

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

0 голосов
/ 03 марта 2018

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

...