Quelle est la différence entre les appels asynchrones et non bloquants? Également entre blocage et appels synchrones (avec des exemples s'il vous plaît)?
Quelle est la différence entre les appels asynchrones et non bloquants? Également entre blocage et appels synchrones (avec des exemples s'il vous plaît)?
Réponses:
Dans de nombreuses circonstances, ce sont des noms différents pour la même chose, mais dans certains contextes, ils sont très différents. Cela dépend donc. La terminologie n'est pas appliquée de manière totalement cohérente dans l'ensemble de l'industrie du logiciel.
Par exemple, dans l'API sockets classiques, une socket non bloquante est celle qui renvoie simplement immédiatement avec un message d'erreur spécial "bloquerait", alors qu'une socket bloquante se serait bloquée. Vous devez utiliser une fonction distincte telle que select
ou poll
pour savoir quel est le bon moment pour réessayer.
Mais les sockets asynchrones (tels que pris en charge par les sockets Windows), ou le modèle d'E / S asynchrone utilisé dans .NET, sont plus pratiques. Vous appelez une méthode pour démarrer une opération et le framework vous rappelle quand c'est fait. Même ici, il existe des différences fondamentales. Les sockets asynchrones Win32 "rassemblent" leurs résultats sur un thread GUI spécifique en passant des messages Windows, tandis que les entrées-sorties asynchrones .NET sont libres (vous ne savez pas sur quel thread votre rappel sera appelé).
Ils ne signifient donc pas toujours la même chose. Pour distiller l'exemple de socket, on pourrait dire:
synchrone / asynchrone est de décrire la relation entre deux modules.
bloquant / non bloquant consiste à décrire la situation d'un module.
Un exemple:
Module X: "I".
Module Y: "librairie".
X demande à Y: avez-vous un livre nommé "c ++ primer"?
1) blocage: avant que Y ne réponde à X, X continue d'attendre la réponse. Maintenant, X (un module) bloque. X et Y sont deux threads ou deux processus ou un thread ou un processus? nous ne savons pas.
2) non bloquant: avant que Y ne réponde à X, X reste simplement là et fait d'autres choses. X peut revenir toutes les deux minutes pour vérifier si Y a terminé son travail? Ou X ne reviendra pas jusqu'à ce que Y l'appelle? Nous ne le savons pas. Nous savons seulement que X peut faire autre chose avant que Y ne termine son travail. Ici, X (un module) est non bloquant. X et Y sont deux threads ou deux processus ou un processus? nous ne savons pas. MAIS nous sommes sûrs que X et Y ne peuvent pas être un seul thread.
3) synchrone: avant que Y ne réponde à X, X continue d'attendre la réponse. Cela signifie que X ne peut pas continuer tant que Y n'a pas terminé son travail. Maintenant, nous disons: X et Y (deux modules) sont synchrones. X et Y sont deux threads ou deux processus ou un thread ou un processus? nous ne savons pas.
4) asynchrone: avant que Y ne réponde à X, X reste là et X peut faire d'autres tâches. X ne reviendra pas jusqu'à ce que Y l'appelle. Maintenant, nous disons: X et Y (deux modules) sont asynchrones. X et Y sont deux threads ou deux processus ou un processus? nous ne savons pas. MAIS nous sommes sûrs que X et Y ne peuvent pas être un seul thread.
Veuillez faire attention aux deux phrases en gras ci-dessus. Pourquoi la phrase en gras du 2) contient-elle deux cas alors que la phrase en gras du 4) ne contient qu'un seul cas? C'est la clé de la différence entre non bloquant et asynchrone.
Voici un exemple typique de non bloquant et synchrone:
// 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);
Vous pouvez voir que cette conception n'est pas bloquante (vous pouvez dire que la plupart du temps cette boucle fait quelque chose de non-sens mais aux yeux du CPU, X est en cours d'exécution, ce qui signifie que X n'est pas bloquant) tandis que X et Y sont synchrones car X peut ne continuez pas à faire d'autres choses (X ne peut pas sortir de la boucle) jusqu'à ce qu'il obtienne le livre de Y.
Normalement dans ce cas, faire le blocage de X est beaucoup mieux parce que le non-blocage dépense beaucoup de ressources pour une boucle stupide. Mais cet exemple est bon pour vous aider à comprendre le fait: le non-blocage ne signifie pas asynchrone.
Les quatre mots nous rendent facilement confus, ce que nous devons retenir, c'est que les quatre mots servent à la conception de l'architecture. Apprendre à concevoir une bonne architecture est le seul moyen de les distinguer.
Par exemple, nous pouvons concevoir un tel type d'architecture:
// 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);
Dans l'exemple ici, nous pouvons dire que
Si vous en avez besoin, vous pouvez également décrire ces threads créés dans X1 avec les quatre mots.
Les choses les plus importantes sont: quand utilisons-nous synchrone au lieu d’asynchrone? quand utilisons-nous le blocage au lieu du non-blocage?
Pourquoi Nginx est-il non bloquant? Pourquoi Apache bloque-t-il?
Pour faire un bon choix, vous devez analyser votre besoin et tester les performances de différentes architectures. Il n'y a pas une telle architecture adaptée à divers besoins.
En plaçant cette question dans le contexte de NIO et NIO.2 dans java 7, async IO est une étape plus avancée que le non-blocage. Avec les appels Java non bloquants, on définirait tous les canaux (SocketChannel, ServerSocketChannel, FileChannel, etc.) comme tels en appelant AbstractSelectableChannel.configureBlocking(false)
. Cependant, après le retour de ces appels d'E / S, vous devrez probablement contrôler les vérifications telles que si et quand lire / écrire à nouveau, etc.
Par exemple,
while (!isDataEnough()) {
socketchannel.read(inputBuffer);
// do something else and then read again
}
Avec l'API asynchrone de Java 7, ces contrôles peuvent être effectués de manière plus polyvalente. L'une des 2 façons est d'utiliser CompletionHandler
. Notez que les deux read
appels ne sont pas bloquants.
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
n'est pas sélectionnable et ne peut pas être configuré pour ne pas bloquer.
Comme vous pouvez probablement le voir dans la multitude de réponses différentes (et souvent mutuellement exclusives), cela dépend de qui vous demandez. Dans certaines arènes, les termes sont synonymes. Ou ils peuvent chacun se référer à deux concepts similaires:
Dans les deux cas, l'intention est de permettre au programme de ne pas être bloqué en attendant la fin d'un processus lent - la façon dont le programme devrait répondre est la seule vraie différence. Quel terme fait référence à celui qui change également de programmeur en programmeur, de langue en langue ou de plateforme en plateforme. Ou les termes peuvent faire référence à des concepts complètement différents (tels que l'utilisation de synchrone / asynchrone en relation avec la programmation des threads).
Désolé, mais je ne crois pas qu'il existe une seule bonne réponse qui soit globalement vraie.
Un appel non bloquant revient immédiatement avec toutes les données disponibles: le nombre total d'octets demandés, moins ou pas du tout.
Un appel asynchrone demande un transfert qui sera effectué dans son intégralité (mais qui se terminera ultérieurement).
Non bloquant: cette fonction n'attendra pas pendant la pile.
Asynchrone: le travail peut se poursuivre au nom de l'appel de fonction une fois que cet appel a quitté la pile
Synchrone est défini comme se produisant en même temps.
Asynchrone est défini comme ne se produisant pas en même temps.
C'est ce qui provoque la première confusion. Synchrone est en fait ce qu'on appelle parallèle. Alors que l'asynchrone est séquentiel, faites ceci, puis faites cela.
Maintenant, tout le problème concerne la modélisation d'un comportement asynchrone, car vous avez une opération qui nécessite la réponse d'un autre avant de pouvoir commencer. C'est donc un problème de coordination, comment saurez-vous que vous pouvez maintenant commencer cette opération?
La solution la plus simple est connue sous le nom de blocage.
Le blocage, c'est quand vous choisissez simplement d'attendre que l'autre chose soit faite et de vous renvoyer une réponse avant de passer à l'opération qui en avait besoin.
Donc, si vous devez mettre du beurre sur du pain grillé, et donc vous devez d'abord faire griller la race. La façon dont vous les coordonneriez est de faire d'abord griller les reproduits, puis de regarder sans cesse le grille-pain jusqu'à ce qu'il saute, puis vous mettriez du beurre dessus.
C'est la solution la plus simple et fonctionne très bien. Il n'y a pas vraiment de raison de ne pas l'utiliser, sauf si vous avez également d'autres choses à faire qui ne nécessitent pas de coordination avec les opérations. Par exemple, faire quelques plats. Pourquoi attendre sans rien regarder le grille-pain en permanence pour que le pain grillé éclate, alors que vous savez que cela prendra un peu de temps, et que vous pourriez laver un plat entier pendant qu'il se termine?
C'est là qu'entrent en jeu deux autres solutions appelées respectivement non bloquantes et asynchrones.
Le non-blocage est lorsque vous choisissez de faire d'autres choses sans rapport avec l'attente de la fin de l'opération. Revoir la disponibilité de la réponse comme bon vous semble.
Donc, au lieu de regarder le grille-pain pour qu'il saute. Vous allez laver un plat entier. Et puis vous regardez le grille-pain pour voir si les toasts ont sauté. Si ce n'est pas le cas, vous allez laver un autre plat, en revenant au grille-pain entre chaque plat. Lorsque vous voyez que les toasts ont éclaté, vous arrêtez de laver la vaisselle, et à la place, vous prenez le toast et passez au beurre.
Devoir vérifier constamment les toasts peut être ennuyeux, imaginez que le grille-pain se trouve dans une autre pièce. Entre les plats, vous perdez votre temps à aller dans cette autre pièce pour vérifier le pain grillé.
Voici asynchrone.
Asynchrone, c'est lorsque vous choisissez de faire d'autres choses sans rapport pendant que vous attendez que l'opération soit effectuée. Au lieu de le vérifier, vous déléguez le travail de vérification à autre chose, pourrait être l'opération elle-même ou un observateur, et cette chose vous avertit et peut-être vous interrompt lorsque la réponse est disponible afin que vous puissiez passer à l'autre opération qui besoin de ceci.
C'est une terminologie bizarre. Cela n'a pas beaucoup de sens, car toutes ces solutions sont des moyens de créer une coordination asynchrone des tâches dépendantes. C'est pourquoi je préfère appeler cela un événement.
Donc, pour celui-ci, vous décidez de mettre à jour votre grille-pain afin qu'il émette un bip lorsque les toasts sont terminés. Il se trouve que vous écoutez constamment, même lorsque vous faites la vaisselle. En entendant le bip, vous faites la queue dans votre mémoire que dès que vous aurez fini de laver votre plat actuel, vous vous arrêterez et irez mettre le beurre sur le pain grillé. Ou vous pouvez choisir d'interrompre le lavage du plat actuel et de traiter le toast immédiatement.
Si vous avez du mal à entendre le bip, vous pouvez demander à votre partenaire de regarder le grille-pain pour vous et de venir vous dire quand le toast est prêt. Votre partenaire peut lui-même choisir l'une des trois stratégies ci-dessus pour coordonner sa tâche de surveiller le grille-pain et de vous dire quand il est prêt.
Sur une note finale, il est bon de comprendre que si le non-blocage et l'async (ou ce que je préfère appeler événementiel) vous permettent de faire d'autres choses pendant que vous attendez, vous n'en avez pas trop. Vous pouvez choisir de boucler constamment en vérifiant l'état d'un appel non bloquant, sans rien faire d'autre. C'est souvent pire que de bloquer (comme regarder dans le grille-pain, puis à l'écart, puis y revenir jusqu'à ce qu'il soit terminé), de nombreuses API non bloquantes vous permettent de passer en mode de blocage à partir de celui-ci. Pour les événements, vous pouvez simplement attendre inactif jusqu'à ce que vous soyez averti. L'inconvénient dans ce cas est que l'ajout de la notification était complexe et potentiellement coûteux au départ. Vous deviez acheter un nouveau grille-pain avec fonction bip ou convaincre votre partenaire de le regarder pour vous.
Et encore une chose, vous devez réaliser les compromis que les trois offrent. L'un n'est évidemment pas meilleur que les autres. Pensez à mon exemple. Si votre grille-pain est si rapide, vous n'aurez pas le temps de laver un plat, pas même de commencer à le laver, c'est à quelle vitesse votre grille-pain est. Commencer sur autre chose dans ce cas n'est qu'une perte de temps et d'efforts. Le blocage fera l'affaire. De même, si le lavage d'un plat prendra 10 fois plus de temps que le grillage. Vous devez vous demander ce qui est le plus important à faire? Le toast pourrait devenir froid et dur à ce moment-là, cela n'en vaut pas la peine, le blocage fera également l'affaire. Ou vous devriez choisir des choses plus rapides à faire pendant que vous attendez. Il y a plus évidemment, mais ma réponse est déjà assez longue, mon point est que vous devez penser à tout cela, et aux complexités de la mise en œuvre de chacun pour décider si cela en vaut la peine et si cela ''
Éditer:
Même si c'est déjà long, je veux aussi qu'il soit complet, donc j'ajouterai deux points supplémentaires.
1) Il existe aussi couramment un quatrième modèle dit multiplexé . C'est lorsque, pendant que vous attendez une tâche, vous en démarrez une autre, et pendant que vous attendez les deux, vous en lancez une autre, et ainsi de suite, jusqu'à ce que vous ayez de nombreuses tâches toutes démarrées, puis, vous attendez inactif, mais sur toutes leur. Ainsi, dès qu'une opération est effectuée, vous pouvez procéder à la gestion de sa réponse, puis recommencer à attendre les autres. C'est ce qu'on appelle le multiplexage, car pendant que vous attendez, vous devez vérifier chaque tâche l'une après l'autre pour voir si elles sont effectuées, ad vitam, jusqu'à ce que l'une soit effectuée. C'est un peu une extension en plus du non-blocage normal.
Dans notre exemple, ce serait comme démarrer le grille-pain, puis le lave-vaisselle, puis le four à micro-ondes, etc. Et puis attendre n'importe lequel d'entre eux. Où vous vérifieriez le grille-pain pour voir si c'est fait, sinon, vous vérifieriez le lave-vaisselle, sinon, le micro-ondes, et encore une fois.
2) Même si je pense que c'est une grosse erreur, synchrone est souvent utilisé pour signifier une chose à la fois. Et beaucoup de choses asynchrones à la fois. Ainsi, vous verrez le blocage synchrone et le non-blocage utilisé pour faire référence au blocage et au non-blocage. Et le blocage asynchrone et le non-blocage faisaient référence aux multiplexés et aux événements.
Je ne comprends pas vraiment comment nous y sommes arrivés. Mais quand il s'agit d'E / S et de calcul, synchrone et asynchrone se réfèrent souvent à ce qui est mieux connu comme non-chevauché et chevauché. Autrement dit, asynchrone signifie que les entrées-sorties et le calcul se chevauchent, c'est-à-dire qu'ils se produisent simultanément. Bien que synchrone signifie qu'ils ne le sont pas, se produisant ainsi de manière séquentielle. Pour le non-blocage synchrone, cela signifie que vous ne démarrez pas d'autres IO ou calcul, vous attendez simplement occupé et simulez un appel de blocage. Je souhaite que les gens cessent de mal utiliser syncrone et asynchrone comme ça. Je ne l'encourage donc pas.
Blocage d' appel: le contrôle ne revient que lorsque l'appel est terminé.
Appel non bloquant : le contrôle revient immédiatement. Le système d'exploitation ultérieur notifie en quelque sorte le processus que l'appel est terminé.
Programme synchrone : programme qui utilise les appels de blocage . Afin de ne pas geler pendant l'appel, il doit avoir 2 threads ou plus (c'est pourquoi il est appelé Synchrone - les threads s'exécutent de manière synchrone).
Programme asynchrone : programme qui utilise des appels non bloquants . Il ne peut avoir qu'un seul thread et reste interactif.
Ils diffèrent uniquement par l'orthographe. Il n'y a aucune différence dans ce à quoi ils se réfèrent. Pour être technique, on pourrait dire qu'ils diffèrent par leur importance. Le non blocage fait référence au flux de contrôle (il ne bloque pas.) Asynchrone fait référence au moment où l'événement \ data est traité (pas de manière synchrone).
Les modèles de blocage nécessitent que l'application initiatrice se bloque au démarrage des E / S. Cela signifie qu'il n'est pas possible de chevaucher le traitement et les E / S en même temps. Le modèle synchrone non bloquant permet le chevauchement du traitement et des E / S, mais il nécessite que l'application vérifie régulièrement l'état des E / S. Cela laisse des E / S non bloquantes asynchrones, ce qui permet le chevauchement du traitement et des E / S, y compris la notification de l'achèvement des E / S.
Blocage: le contrôle revient à l'invocation de la précession après la fin du traitement de la primitive (synchronisation ou async)
Non bloquant: le contrôle revient au processus immédiatement après l'appel