Quelle est la différence entre:
Quelque chose peut-il être à la fois asynchrone et non bloquant (et basé sur les événements)?
Qu'est-ce qui est le plus important en programmation, d'avoir quelque chose: asynchrone, non bloquant et/ou basé sur des événements (ou les 3)?
Si vous pouviez fournir des exemples, ce serait formidable.
Cette question est posée parce que je lisais cet excellent article StackOverflow sur un sujet similaire, mais il ne répond pas à mes questions ci-dessus.
Asynchrone Asynchrone signifie littéralement pas synchrone. Le courrier électronique est asynchrone. Vous envoyez un mail, vous ne vous attendez pas à recevoir une réponse MAINTENANT. Mais ce n'est pas non bloquant. Essentiellement, cela signifie une architecture où les "composants" s'envoient des messages sans attendre de réponse immédiate. Les requêtes HTTP sont synchrones. Envoyez une demande et obtenez une réponse.
Non-Blocking Ce terme est principalement utilisé avec IO. Cela signifie que lorsque vous effectuez un appel système, il revient immédiatement avec le résultat obtenu sans mettre votre thread en veille (avec une probabilité élevée). Par exemple, les appels en lecture/écriture non bloquants retournent avec tout ce qu'ils peuvent faire et s'attendent à ce que l'appelant exécute à nouveau l'appel. try_lock par exemple est un appel non bloquant. Il ne se verrouillera que si le verrouillage peut être acquis. La sémantique habituelle pour les appels systèmes est bloquante. read attendra d'avoir des données et mettra en veille le thread appelant.
Event-base Ce terme vient de libevent. les appels non bloquants en lecture/écriture en eux-mêmes sont inutiles car ils ne vous disent pas "quand" devez-vous les rappeler (réessayer). select/epoll/IOCompletionPort etc. sont des mécanismes différents pour découvrir à partir du système d'exploitation "quand" ces appels devraient renvoyer des données "intéressantes". libevent et d'autres bibliothèques de ce type fournissent des wrappers sur ces fonctions de surveillance des événements fournies par divers systèmes d'exploitation et fournissent une API cohérente avec laquelle s'exécuter sur les systèmes d'exploitation. Non bloquant IO va de pair avec Event-base.
Je pense que ces termes se chevauchent. Par exemple, le protocole HTTP est synchrone mais l'implémentation HTTP utilisant un non-blocage IO peut être asynchrone. Encore une fois, un appel d'API non-bloquant comme read/write/try_lock est synchrone (il donne immédiatement une réponse) mais la "gestion des données" est asynchrone.
Dans un matériel asynchrone, le code demande à une entité de faire quelque chose et est libre de faire d'autres choses pendant que l'action se fait; une fois l'action terminée, l'entité signalera généralement le code d'une certaine manière. Une architecture non bloquante notera les actions spontanées qui pourraient intéresser le code et permettra au code de demander quelles actions ont eu lieu, mais le code ne prendra connaissance de ces actions que lorsqu'il le demandera explicitement. Une architecture basée sur des événements avertit le code de manière positive lorsque des événements se produisent spontanément.
Prenons un port série, dont le code voudra recevoir 1 000 octets.
Dans une architecture à lecture bloquée, le code attendra jusqu'à ce que 1000 octets soient arrivés ou qu'il décide d'abandonner.
Dans une architecture à lecture asynchrone, le code indiquera au pilote qu'il souhaite 1 000 octets et sera averti lorsque 1 000 octets seront arrivés.
Dans une architecture non bloquante, le code peut demander à tout moment combien d'octets sont arrivés, et peut lire une ou toutes ces données quand il le juge opportun, mais la seule façon de savoir quand toutes les données sont arrivées est de demander; si le code veut savoir dans un quart de seconde lorsque le 1000e octet est arrivé, il doit vérifier tous les quarts de seconde environ.
Dans une architecture basée sur les événements, le pilote de port série avertira l'application à chaque arrivée de données. Le pilote ne saura pas combien d'octets l'application veut, donc l'application doit être capable de traiter les notifications pour des quantités plus petites ou plus grandes que ce que l'application veut.
Pour moi, le non-blocage signifie que l'exécution d'une action dans un thread ne dépend pas de l'exécution d'autres threads, elle ne nécessite notamment pas de section critique.
Asynchrone signifie que l'exectuion se produit en dehors du flux de l'appelant et est potentiellement différée. L'exécution se produit généralement dans un autre thread.
La lecture de données simultanées n'est pas bloquante (pas besoin de verrouiller), mais synchrone. Inversement, l'écriture simultanée de données de manière synchrone est bloquante (nécessite un verrou exclusif). Un moyen de le rendre non bloquant du point de vue du flux principal consiste à rendre les écritures asynchrones et à différer leur exécution.
Le concept d'événement est autre chose, ce qui signifie grosso modo que vous êtes informé lorsque quelque chose se produit. Si les écritures ont été exécutées de manière asynchrone, un événement peut être déclenché pour informer les autres parties du système une fois l'écriture exécutée. Les autres parties répondront à l'événement. Le système peut être construit uniquement sur des événements comme le seul moyen de communiquer entre les composants (pensez au modèle d'acteur), mais cela ne doit pas nécessairement être le cas.
Les trois termes sont liés, mais ce sont des concepts différents pour moi. Il se peut cependant que les gens les utilisent de manière quelque peu interchangeable.
En règle générale, une architecture non bloquante est basée sur des appels de méthode qui, bien qu'ils puissent s'exécuter pendant longtemps sur le thread travailleur, ne bloquent pas le thread appelant. Si le thread appelant doit acquérir des informations sur ou à partir de la tâche exécutée par le thread de travail, c'est au thread appelant de le faire.
Une architecture basée sur des événements est basée sur le concept de code exécuté en réponse à des événements qui sont déclenchés. Le moment de l'exécution du code n'est généralement pas déterministe, mais les événements peuvent invoquer des méthodes de blocage; ce n'est pas parce qu'un système est basé sur des événements que tout ce qu'il fait n'est pas bloquant.
Généralement, une architecture asynchrone est une architecture non bloquante basée sur des événements.
Lorsqu'un appel asynchrone est effectué, les gestionnaires d'événements sont enregistrés auprès de l'API fournissant des services de synchronisation, afin d'avertir l'appelant que quelque chose l'intéressant s'est produit. L'appel retourne ensuite immédiatement (comportement non bloquant) et l'appelant est libre de poursuivre l'exécution. Lorsque les événements sont renvoyés vers le processus appelant, ils seront traités sur un thread de ce processus.
Il est important de comprendre si les événements seront traités sur le même thread ou non, car cela affectera la nature non bloquante de l'exécution, mais je ne connais personnellement aucune bibliothèque qui gère la gestion asynchrone sur un seul thread.
J'ai supprimé le paragraphe ci-dessus car il n'est pas strictement correct comme indiqué. Mon intention était de dire que même si les opérations dans le système ne sont pas bloquantes, telles que les appels vers une installation du système d'exploitation et la poursuite de l'exécution, le la nature de l'exécution monothread signifie que lorsque des événements sont déclenchés, ils seront en concurrence avec d'autres tâches de traitement pour le temps de calcul sur le thread.
Donc, pour répondre à votre première et deuxième question:
Le non-blocage est en fait identique à l'asynchrone - vous passez l'appel, et vous obtiendrez un résultat plus tard, mais pendant ce temps, vous pouvez faire autre chose. Le blocage est le contraire. Vous attendez le retour de l'appel avant de poursuivre votre voyage.
Maintenant, le code asynchrone/non bloquant semble absolument fantastique, et c'est le cas. Mais j'ai des mots d'avertissement. L'async/non-blocage est excellent lorsque vous travaillez dans des environnements contraints, comme dans un téléphone mobile ... pensez à un processeur/mémoire limité. C'est également bon pour le développement frontal, où votre code doit réagir à un widget d'interface utilisateur d'une manière ou d'une autre.
L'async est fondamental pour la façon dont tous les systèmes d'exploitation doivent fonctionner - ils font la merde pour vous en arrière-plan et réveillent votre code lorsqu'ils ont fait ce que vous avez demandé, et lorsque cet appel échoue, on vous dit que ce n'est pas le cas travailler soit par une exception, soit par une sorte d'objet code de retour/erreur.
Au moment où votre code demande quelque chose qui prendra un certain temps à répondre, votre système d'exploitation sait qu'il peut être occupé à faire d'autres choses. Votre code - un processus, thread ou équivalent, bloque. Votre code est totalement inconscient de ce qui se passe dans le système d'exploitation pendant qu'il attend que cette connexion réseau soit établie, ou pendant qu'il attend cette réponse d'une demande HTTP, ou pendant qu'il lit/écrit un fichier, et bientôt. Votre code pourrait "simplement" attendre un clic de souris. Ce qui se passait en réalité pendant cette période, c'est que votre système d'exploitation gère, planifie et réagit de manière transparente aux "événements" - des choses que le système d'exploitation recherche, telles que la gestion de la mémoire, des E/S (clavier, souris, disque, Internet), autres tâches, reprise après incident, etc.
Les systèmes d'exploitation sont durs à cuire. Ils sont vraiment bons pour cacher tous les trucs asynchrones/non bloquants compliqués à vous le programmeur. Et c'est ainsi que la plupart des programmeurs en sont arrivés là où nous en sommes aujourd'hui avec les logiciels. Maintenant que nous atteignons les limites du processeur, les gens disent que les choses peuvent être faites en parallèle pour améliorer les performances. Cela signifie que Async/non-blocking semble être une chose très favorable à faire, et oui, si votre logiciel l'exige, je suis d'accord.
Si vous écrivez un serveur Web principal, procédez avec prudence. N'oubliez pas que vous pouvez évoluer horizontalement pour beaucoup moins cher. Netflix/Amazon/Google/Facebook sont des exceptions évidentes à cette règle, uniquement parce que cela fonctionne moins cher pour eux d'utiliser moins de matériel.
Je vais vous dire pourquoi le code asynchrone/non bloquant est un cauchemar avec les systèmes back-end ....
1) Cela devient un déni de service sur la productivité ... vous devez penser BEAUCOUP plus, et vous faites beaucoup d'erreurs en cours de route.
2) Les traces de pile dans le code réactif deviennent indéchiffrables - il est difficile de savoir ce qu'on appelle quoi, quand, pourquoi et comment. Bonne chance pour le débogage.
3) Vous devez réfléchir davantage à la façon dont les choses échouent, surtout lorsque beaucoup de choses ne correspondent plus à la façon dont vous les avez envoyées. Dans l'ancien monde, vous faisiez une chose à la fois.
4) C'est plus difficile à tester.
5) C'est plus difficile à entretenir.
6) C'est douloureux. La programmation devrait être une joie et un plaisir. Seuls les masochistes aiment la douleur. Les personnes qui écrivent des cadres simultanés/réactifs sont des sadiques.
Et oui, j'ai écrit à la fois sync et async. Je préfère la synchronisation car 99,99 des applications back-end peuvent s'en sortir avec ce paradigme. Les applications frontales ont besoin de code réactif, sans aucun doute, et cela a toujours été le cas.
Oui, le code peut être asynchrone, non bloquant ET basé sur des événements.
La chose la plus importante dans la programmation est de s'assurer que votre code fonctionne et répond dans un délai acceptable. Respectez ce principe clé et vous ne pouvez pas vous tromper.