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

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

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

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

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

4 ответов


так что же на самом деле"неблокирующий асинхронный ввод-вывод"?

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

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


Асинхронные Группы Каналов

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

размер threadpool, который поддерживает группу асинхронных каналов, настраивается следующим свойством 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: я должен упомянуть, что поддержка асинхронного NIO появилась в JDK 1.7


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

есть четыре типа ИО, относящиеся к этому обсуждению:

блокирование IO

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

асинхронный ввод / вывод

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


Я бы сказал, что есть три типа ввода-вывода:

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

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

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


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

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

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

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

блокировка асинхронного ввода-вывода

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

неблокирующий асинхронный ИО

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

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

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