web-dev-qa-db-fra.com

non bloquant IO vs async IO et implémentation en Java

Essayer de résumer moi-même la différence entre ces 2 concepts (parce que je suis vraiment confus quand je vois que les gens les utilisent tous les deux en une phrase, comme "entrées-sorties asynchrones non bloquantes" dont j'essaie de comprendre le rôle signifier).

Donc, à ma connaissance, non-bloquant IO est principal le mécanisme du système d’exploitation pour traiter le IO s’il existe des données prêtes, sinon il suffit de renvoyer l’erreur/ne rien faire. 

En mode asynchrone IO, vous fournissez simplement un rappel et votre application sera avertie lorsque les données seront disponibles.

Alors, qu’est-ce que c’est "un E/S async non-bloquant"? Et comment tous peuvent être implémentés en Java (JDK standard, sans bibliothèques externes, je sais qu’il existe Java.nio.channels.{Channels, Selector, SelectorKey} et Java.nio.channels.{AsynchronousSocketChannel}): IO non bloquant, IO asynchrone et asynchrone non bloquant IO (s’il existe )?

48
akazlou

Alors qu'est-ce qui est réellement "IO asynchrone non bloquant"? 

Pour répondre à cette question, vous devez d’abord comprendre qu’il n’existe pas de blocage d'E/S asynchrones. Le concept même de l'asynchronisme dicte qu'il n'y a pas d'attente, pas de blocage, pas de retard. Lorsque vous voyez E/S asynchrones non bloquantes, le bit non bloquant sert uniquement à qualifier davantage l'adjectif asynchrone dans ce terme. Si bien que _ {E/S asynchrones non bloquantes} pourrait être un peu une redondance.

Il existe principalement deux types d'E/S. Synchronous et Asynchronous. Synchronous bloque le thread d'exécution en cours jusqu'à la fin du traitement, tandis que Asynchronous ne bloque pas le thread en cours d'exécution, mais transmet le contrôle au noyau du système d'exploitation pour un traitement ultérieur. Le noyau informe ensuite le thread async lorsque la tâche soumise est terminée


Groupes de canaux asynchrones

Le concept de canaux asynchrones en Java repose sur des groupes de canaux asynchrones. Un groupe de canaux asynchrones regroupe en principe plusieurs canaux à réutiliser. Les utilisateurs de l'API asynchrone récupèrent un canal du groupe (la machine virtuelle Java en crée un par défaut) et le canal se réintroduit automatiquement dans le groupe une fois son opération de lecture/écriture terminée. En fin de compte, les groupes de canaux asynchrones sont pris en charge par surprise, threadpools. De plus, les canaux asynchrones sont threadsafe. 

La taille du pool de threads qui sauvegarde un groupe de canaux async est configurée par la propriété JVM suivante

Java.nio.channels.DefaultThreadPool.initialSize

qui, étant donné un nombre entier, configurera un pool de threads de cette taille, pour sauvegarder le groupe de canaux. Sinon, le groupe de canaux est créé et maintenu de manière transparente pour le développeur. 


Et comment tous peuvent être implémentés en Java

Eh bien, je suis content que vous avez demandé. Voici un exemple de AsynchronousSocketChannel (utilisé pour ouvrir un client non bloquant Socket sur un serveur d'écoute.) Cet exemple est un extrait de Apress Pro Java NIO.2 , commenté par moi:

//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: Je devrais mentionner que le support pour Async NIO est venu dans JDK 1.7

43
kolossus

Je dirais qu'il y a trois types d'io:

blocage synchrone
synchrone non bloquant
asynchrone

Les méthodes synchrone non bloquante et asynchrone sont considérées comme non bloquantes, car le thread appelant n'attend pas la fin de IO. Ainsi, bien que les io asynchrones non bloquants puissent être redondants, ils ne sont pas identiques. Lorsque j'ouvre un fichier, je peux l'ouvrir en mode non bloquant. Qu'est-ce que ça veut dire? Cela signifie que quand je lance un read (), il ne bloquera pas. Cela me retournera les octets disponibles ou indiquera qu'il n'y en a pas. Si je n'active pas le io non bloquant, read () bloquera jusqu'à ce que les données soient disponibles. Je pourrais vouloir activer io non bloquant si je veux qu'un thread gère plusieurs requêtes io. Par exemple, je pourrais utiliser select () pour savoir quels descripteurs de fichiers, ou peut-être de sockets, disposent de données à lire. Je fais ensuite des lectures synchrones sur ces descripteurs de fichiers. Aucune de ces lectures ne devrait bloquer, car je sais déjà que des données sont disponibles. De plus, j'ai ouvert les descripteurs de fichier en mode non bloquant.

Io asynchrone est l'endroit où vous émettez une demande io. Cette demande est en file d'attente et ne bloque donc pas le thread émetteur. Vous êtes averti lorsque la demande a échoué ou s'est terminée avec succès.

4
nickdu

Non bloquant IO signifie que l'appel à exécuter IO est renvoyé immédiatement et ne bloque pas votre thread.

La seule façon de savoir si le IO est terminé, consiste à interroger son statut ou son blocage. Pensez-y comme une Future. Vous démarrez une opération IO et il vous renvoie une Future. Vous pouvez appeler isDone() dessus pour vérifier si c'est fait, si c'est le cas, faites ce que vous voulez, sinon continuez à faire d'autres choses jusqu'à la prochaine fois que vous voudrez vérifier si c'est fait. Ou, si vous n’êtes plus à faire, vous pouvez appeler get, ce qui bloquera jusqu’à ce que ce soit fait.

Async IO signifie que l'appel à exécuter IO vous avertit que l'opération est effectuée via un événement et non via sa valeur de retour.

Cela peut être bloquant ou non bloquant.

Async bloquant IO

Par blocage async IO, on entend que l’appel à exécuter IO est un appel bloquant normal, mais la chose que vous avez appelée est encapsulée dans un fil qui bloquera jusqu’à ce que IO soit terminé, puis déléguez le traitement du résultat du IO à votre rappel. C'est-à-dire qu'il y a toujours un fil inférieur dans la pile qui est bloqué sur l'IO, mais que votre fil ne l'est pas.

Async non bloquant IO

Il s’agit en fait du problème le plus courant. Cela signifie que l’état du statut IO non bloquant n’a pas besoin d’être interrogé, comme pour les E/S standard non bloquantes; il vous appellera à la fin de son rappel. Contrairement au blocage des entrées-sorties asynchrones, celui-ci n'a pas de threads bloqués dans la pile. Il est donc plus rapide et utilise moins de ressources, car le comportement asynchrone est géré sans blocage des threads.

Vous pouvez penser à cela comme une CompletableFuture. Pour cela, votre programme doit disposer d’une structure d’événement asynchrone, multithread ou non. Il est donc possible que le rappel soit exécuté dans un autre thread ou qu'il soit programmé pour une exécution sur un thread existant une fois la tâche en cours terminée.

J'explique la distinction plus en détail ici.

1
Didier A.