Подтвердить что ты не робот

Неблокирующий IO vs async IO и реализация в Java

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

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

В async IO вы просто предоставляете обратный вызов, и ваше приложение будет уведомлено, когда данные будут доступны.

Итак, что на самом деле является "неблокирующим асинхронным IO"? И как все они могут быть реализованы на Java (стандартный JDK, без внешних библиотек, я знаю, что есть java.nio.channels.{Channels, Selector, SelectorKey} и java.nio.channels.{AsynchronousSocketChannel}): неблокирующий IO, async IO и неблокирующий async IO (если есть такая вещь )?

4b9b3361

Ответ 1

Итак, что на самом деле является "неблокирующим асинхронным IO"?

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

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


Асинхронные группы каналов

Концепция Async Channels в java поддерживается асинхронными группами каналов. Группа асинхронных каналов в основном объединяет несколько каналов для повторного использования. Потребители async api извлекают канал из группы (JVM создает один по умолчанию), и канал автоматически возвращается в группу после завершения операции чтения/записи. В конечном счете, группы Async Channel поддерживаются неожиданностью, threadpools. Кроме того, асинхронные каналы являются потокобезопасными.

Размер пула потоков, который поддерживает группу асинхронных каналов, настраивается следующим свойством JVM

java.nio.channels.DefaultThreadPool.initialSize

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


И как все они могут быть реализованы в Java

Хорошо, я рад, что вы спросили. Здесь приведен пример AsynchronousSocketChannel (используемый для открытия неблокирующего клиента Socket на сервере прослушивания.) Этот образец является выдержкой из Apress Pro Java NIO.2, прокомментировал меня:

//Create an Asynchronous channel. No connection has actually been established yet
AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open(); 

/**Connect to an actual server on the given port and address. 
   The operation returns a type of Future, the basis of the all 
   asynchronous operations in java. In this case, a Void is 
   returned because nothing is returned after a successful socket connection
  */
Void connect = asynchronousSocketChannel.connect(new InetSocketAddress("127.0.0.1", 5000)).get();


//Allocate data structures to use to communicate over the wire
ByteBuffer helloBuffer = ByteBuffer.wrap("Hello !".getBytes()); 

//Send the message

Future<Integer> successfullyWritten=  asynchronousSocketChannel.write(helloBuffer);

//Do some stuff here. The point here is that asynchronousSocketChannel.write() 
//returns almost immediately, not waiting to actually finish writing 
//the hello to the channel before returning control to the currently executing thread

doSomethingElse();

//now you can come back and check if it was all written (or not)

System.out.println("Bytes written "+successfullyWritten.get());

EDIT: я должен упомянуть, что поддержка Async NIO появилась в JDK 1.7

Ответ 2

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

В этом обсуждении есть четыре типа IO:

Блокировка ввода-вывода

Неблокирующий IO

Асинхронный IO

Асинхронный неблокирующий IO

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

Сначала Давайте поговорим об IO. Когда мы имеем медленный IO, это наиболее очевидно, но операции ввода-вывода могут быть либо блокирующими, либо неблокирующими. Это не имеет ничего общего с потоками, это связано с интерфейсом к операционной системе. Когда я запрашиваю ОС для операции ввода-вывода, у меня есть выбор для ожидания готовности всех данных (блокировки) или получения того, что доступно прямо сейчас и перемещения ( неблокирование). По умолчанию используется блокировка ввода-вывода. Гораздо проще писать код, используя блокировку IO, поскольку путь намного яснее. Однако ваш код должен остановиться и дождаться завершения ввода-вывода. Non-Blocking IO требует взаимодействия с библиотеками IO на более низком уровне, используя select и read/write вместо библиотек более высокого уровня, которые обеспечивают удобные операции. Non-Blocking IO также подразумевает, что у вас есть что-то, над чем вам нужно работать, пока ОС работает при выполнении ввода-вывода. Это может быть несколько операций ввода-вывода или вычисление на IO, которое завершилось.

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

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

Теперь асинхронный режим начинается с путаницы. Общая концепция асинхронности подразумевает, что процесс продолжается во время фоновой операции, механизм, с помощью которого это происходит, не является конкретным. Этот термин неоднозначен, поскольку как неблокирующее IO, так и блокирование ввода-вывода IO можно считать асинхронным. Оба допускают параллельные операции, однако требования к ресурсам различны, а код существенно отличается. Поскольку вы задали вопрос "Что такое блокировка асинхронного ввода-вывода", я собираюсь использовать более строгие определения для асинхронной, многопоточной системы, выполняющей IO, которая может быть или не быть блокирующей.

Общее определение

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

Более строгое определение

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

Теперь с этими более ясными определениями мы имеем следующие четыре типы парадигм IO.

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

Асинхронный IO - Threaded IO, в котором приложение использует потоки выполнения для одновременного выполнения операций блокировки ввода-вывода. Требуется безопасный код потока, но, как правило, легче читать и писать, чем альтернатива. Получает накладные расходы нескольких потоков, но имеет четкие пути выполнения. Может потребоваться использование синхронизированных методов и контейнеров.

Non-Blocking IO - однопоточный IO, в котором приложение использует выбор для определения того, какие операции ввода-вывода готовы к продвижению, что позволяет выполнять другой код или другие операции ввода-вывода, в то время как ОС обрабатывает одновременный ввод-вывод, Процесс не спящий, ожидая прерывания ввода-вывода, но берет на себя ответственность проверять флаг IO на дескрипторе файла. Гораздо более сложный код из-за необходимости проверять флаг IO с помощью select, хотя не требует потокобезопасного кода или синхронизированных методов и контейнеров. Низкое исполнение над головой за счет сложности кода. Пути выполнения свернуты.

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

Ответ 3

Я бы сказал, что существует три типа io:

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

Оба синхронных неблокирующих и асинхронных будут считаться неблокирующими, поскольку вызывающий поток не ожидает завершения ввода-вывода. Таким образом, хотя неблокирующий асинхронный io может быть избыточным, они не являются одними и теми же. Когда я открываю файл, я могу открыть его в неблокирующем режиме. Что это значит? Это означает, что когда я выдаю read(), он не будет блокироваться. Он либо вернет мне доступные байты, либо покажет, что байтов нет. Если я не включил неблокирующий io, read() будет блокироваться до тех пор, пока данные не будут доступны. Возможно, я хочу включить неблокирующий io, если я хочу, чтобы поток обрабатывал несколько запросов io. Например, я мог бы использовать select(), чтобы узнать, какие файловые дескрипторы или, возможно, сокеты, имеют доступные для чтения данные. Затем я выполняю синхронное чтение этих дескрипторов файлов. Ни одно из этих чтений не должно блокироваться, потому что я уже знаю, что данные доступны, плюс я открыл дескрипторы файлов в неблокирующем режиме.

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

Ответ 4

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

Единственный способ узнать, выполняется ли IO, - опросить его статус или заблокировать. Подумайте об этом как Future. Вы запускаете операцию ввода-вывода, и она возвращает вам Future. Вы можете называть isDone() на нем, чтобы проверить, выполнено ли это, если это так, делать то, что вы хотите с ним, иначе продолжайте делать другие вещи до следующего раза, когда вы захотите проверить, выполнено ли это. Или, если вам нечего делать, вы можете вызвать get на нем, который будет блокироваться до его завершения.

Async IO - это когда вызов выполнить IO уведомляет вас, что это делается через событие, а не через его возвращаемое значение.

Это может быть блокировка или неблокирование.

Блокировка Async IO

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

Non-blocking Async IO

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

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

Я объясняю различие более подробно здесь.