Exemples concrets
J'aimerais ajouter quelques exemples concrets et les connecter au monde du génie logiciel. Premièrement, considérez quelque chose qui, j'espère, correspond à votre définition intuitive de "synchrone": le clignotement de lucioles , dans certaines circonstances. Deuxièmement, considérons la course de relais olympique 4x100 féminin . Troisièmement, considérez ce vieux trope des films militaires: "Hommes, synchronisez vos montres!"
Maintenant, réfléchissons à ce qui se passe. Commençons par observer que toutes ces choses sont des processus ou des entités étendues dans le temps . Cela n'a pas de sens de dire qu'un bol est "synchrone" et que le rock est "asynchrone". Deuxièmement, il faut être deux pour danser le tango . Vous ne pouvez pas dire que "un coureur est sync". Sync avec quoi? Enfin, pour que deux processus puissent agir en même temps, à moins qu’ils aient déjà exactement la même fréquence et la même phase, il faut attendre l’un ou l’autre .
Une analyse
Lorsque la définition du dictionnaire indique que deux entités synchronisées "se produisent ou existent en même temps", cela s'aligne très bien sur le concept de la lumière émise par les lucioles. Malheureusement, dire que la lumière est "synchronisée" est une façon bâclée de dire que les processus d’éclairage de la luciole sont synchronisés.
Alors, comment un groupe de lucioles, qui n’ont vraisemblablement pas été guidées par Apple SmartWatch et NTP, parviennent-elles à flasher leurs arrières en même temps? Eh bien, c'est assez facile s'ils ont le moyen de définir un tempo cohérent et peuvent y apporter de petits ajustements. Ils clignotent simplement, et si plus de personnes clignotent juste après eux, ils ralentissent (augmentent le délai), alors que si plus de personnes clignotent juste avant eux, ils accélèrent (diminuent le délai). Ils peuvent donc utiliser un processus de retour simple pour parvenir essentiellement au même tempo et à la même phase. L'observation importante ici est de noter qu'ils réalisent la synchronie en attendant le bon moment pour clignoter .
La course 4x100 est intéressant parce que vous voyez les deux formes de synchronisation de processus en action: les coureurs au sein d' une équipe sont synchronisés, tandis que les coureurs des équipes différentes sont « async ». Le deuxième coureur du relais doit attendre que le premier coureur entre dans la zone de transfert . Le transfert est un événement synchrone entre ces deux coureurs. Cependant, les coureurs dans les différentes voies ne s’inquiètent pas de ce qui se passe dans une autre voie , et très certainement ne ralentissent pas et ne font pas leurs transferts de manière synchronisée. Chaque piste de coureurs est asynchrone les uns par rapport aux autres. Là encore, nous voyons que la synchronisation implique l'attente, contrairement à l'asynchronisme.
Enfin, les soldats d'une compagnie (peloton, équipe de tir, etc.) doivent synchroniser leurs montres pour pouvoir attaquer l'ennemi en même temps . Il se peut que certains soldats arrivent à leurs positions avant d’autres, ou aient la possibilité de tirer plus rapidement sur l’ennemi. Mais une attaque simultanée est généralement plus efficace qu'une attaque au hasard en raison de l'élément de surprise. Donc, pour parvenir à la synchronie, beaucoup de soldats doivent attendre le temps imparti pour agir.
Caractéristique déterminante
Pourquoi cette insistance sur l'attente? Eh bien, c’est parce que l’attente est la caractéristique qui distingue les processus synchrones des processus asynchrones. Si vous avez deux processus dont vous ne connaissez rien, vous devez, par défaut, supposer qu'ils sont asynchrones. Par exemple, une livraison de colis et une ambulance ne sont probablement pas synchronisées. Pour démontrer que deux processus sont en fait synchronisés, vous devez trouver un moment très spécial dans le temps: le point de synchronisation .
Un livreur qui dépose un colis et une ambulance qui dépêche quelqu'un à l'hôpital ne partage généralement pas les points de temps que nous identifions comme un "point de synchronisation". D'autre part, les lucioles clignotant à l'unisson ont un point de synchronisation chaque fois qu'elles clignotent, les relais ont un point de synchronisation chaque fois qu'elles passent le relais, et les soldats ont un point de synchronisation lorsqu'ils lancent leur attaque. Si vous pouvez identifier un ou plusieurs points de synchronisation, les processus sont synchronisés . Cela devrait être facile à comprendre, car "syn-" est un préfixe grec qui signifie "avec" ou "ensemble", et "chrono" est la racine grecque du mot "temps". "Synchronisé" signifie littéralement "en même temps",
Limites
Notez que la "synchronisation" ne s'applique pas nécessairement à toute la durée de vie de l'un ou des deux processus. Je dirais que cela ne concerne que "le temps d’attente jusqu’au point de synchronisation compris". Ainsi, deux processus peuvent fonctionner de manière asynchrone jusqu'à ce qu'ils atteignent un état où ils doivent communiquer, puis ils se synchronisent, échangent des informations et poursuivent ensuite de manière asynchrone. Un exemple simple est de rencontrer quelqu'un pour un café. De toute évidence, la réunion est un point de synchronisation (ou plusieurs, plutôt), et le fait que deux personnes y arrivent témoigne du synchronisme. Cependant, nous ne dirions pas que, parce que deux personnes se sont rencontrées pour prendre un café, ces deux vies humainessont "synchronisés". C’est peut-être le seul moment de leur vie qu’ils ont rencontré et tout ce qu’ils font est par ailleurs indépendant.
Il n’est pas vrai non plus que des rencontres accidentelles démontrent une synchronie. Si deux étrangers se croisent dans la rue, le fait qu'ils se trouvent à un moment donné ne prouve pas le synchronisme. Le fait qu’une personne soit assise sur un banc et attende l’autobus ne l’est pas non plus. Les processus ne sont synchrones que lorsqu'ils se rencontrent dans un but précis .
Connexion de logiciel
Penchons-nous maintenant sur une tâche fondamentale du logiciel: lire à partir d’un fichier. Comme vous le savez probablement, le stockage de masse est généralement des milliers, des millions de fois plus lent que le cache ou la mémoire principale. Pour cette raison, les systèmes d'exploitation et les bibliothèques de langages de programmation offrent généralement des opérations d'E / S synchrones et asynchrones. Maintenant, même si votre programme n'a qu'un seul thread, vous devriez considérer le système d'exploitation comme un "processus séparé" aux fins de cette discussion.
Sync
Lorsque vous effectuez une "lecture E / S synchrone", votre thread doit attendre que les données soient disponibles, puis il continue. Cela ressemble beaucoup à un coureur de relais qui passe le relais au prochain coureur, mais imaginons plutôt un relais avec seulement deux coureurs faisant tout le tour de la piste, et le deuxième coureur revenant également au premier.
Dans ce cas, votre thread de programme et le processus d'ES du système d'exploitation ne se produisent pas en même temps, il est donc étrange de dire que ces processus sont "synchronisés". Mais ce n'est pas la bonne façon de voir les choses! C'est comme si on disait: "Les coureurs d'une équipe de relais ne courent pas en même temps, ils ne sont donc pas synchronisés." En fait, les deux déclarations sont fausses! Les coureurs sur une équipe de relais font et doivent fonctionner en même temps, mais seulement à un moment très précis: la main hors du bâton. En fait, ce n'est que ce moment privilégié de la course qui nous convainc que les équipes de relais sont synchronisées pour commencer! Si nous considérons la demande d’entrée / sortie et la réponse comme "le témoin",
D'autre part, si nous pensons à quelque chose comme l'analyse par éléments finis sur un superordinateur, nous voyons que des milliers de processus doivent fonctionner en bloc pour mettre à jour un état global massif. Même si certains nœuds terminent leur travail pour un pas de temps donné avant les autres, ils doivent tous attendre que le pas de temps se termine car les résultats se propagent aux voisins à travers l'espace. Ce type de synchronisation ressemble aux lucioles: tous les acteurs effectuent le même type de tâche.
Variété de processus
Pour cette raison, nous pouvons inventer quelques termes pour nous aider à voir qu'il y a trois sortes de choses qui se passent: "synchronie homogène", "synchronie hétérogène" et "synchronie séquentielle". Ainsi, lorsque les acteurs effectuent la même tâche simultanément (FEA, lucioles), ils sont "homogènes". Lorsqu'ils exécutent simultanément différentes tâches (soldats qui courent ou rampent ou qui nagent vers leur destination, la physique, le son ou les fils d'IA dans un jeu), ils sont "hétérogènes". Lorsqu'ils effectuent des tâches une à une, ils sont "séquentiels" (relais, bloqueurs d'E / S). Ils peuvent sembler très différents, mais ils partagent une propriété essentielle: tous les types d’acteurs en attendent pour s’assurer que tout le monde arrive au point de synchronisation en même temps. entre les points de synchronisation ou "effectuer la même action" est sans rapport avec la propriété de synchronicity.
Les pipelines de rendu dans un GPU sont synchrones car ils doivent tous terminer l’image ensemble et en commencer une nouvelle. Ils sont homogènes parce qu'ils font le même genre de travail et ils sont tous actifs ensemble. Mais la boucle de jeu principale d'un serveur et les threads d'E / S bloquants qui traitent les entrées distantes sont hétérogènes car ils effectuent des types de travail très différents, et certains des threads d'E / S ne feront rien du tout, car tous les connexions sont utilisées. Même dans ce cas, ils sont synchronisés, car ils doivent partager l'état de manière atomique (un joueur ne doit pas voir une mise à jour partielle du monde du jeu, ni le serveur ne doit voir qu'un fragment de son entrée).
Async
Considérons maintenant une "lecture asynchrone d'E / S". Lorsque votre programme envoie une demande au système d'exploitation pour lire un bit de données de la mémoire, l'appel retourne immédiatement . Ignorons les rappels et concentrons-nous sur la scrutation. En général, le moment où les données sont disponibles pour votre programme ne correspond à aucun instant particulier en ce qui concerne le fil de votre programme. Si votre programme n'attend pas explicitement les données, le thread ne saura même pas exactement quand ce moment se produira. Il ne découvrira que les données sont en attente lors de la prochaine vérification.
Il n’ya pas d’heure de réunion spéciale où le système d’exploitation et le fil de programmation s’entendent pour transmettre les données. Ils sont comme deux navires qui passent dans la nuit. L'asynchronisme est caractérisé par cette absence d'attente. Bien entendu, le thread de programme finit souvent par attendre l'attente de l'opération d'E / S, mais ce n'est pas nécessaire. Il peut continuer à effectuer d'autres calculs alors que l'extraction d'E / S est en cours, et ne vérifie que plus tard s'il a un moment à perdre. Bien sûr, une fois que le système d’exploitation a fini de récupérer les données, il n’attend pas non plus. Il met simplement les données à un endroit pratique et traite de son activité. Dans ce cas, c’est comme si le programme passait le témoin au système d’exploitation, lequel revenait plus tard, lâchait le témoin au sol avec les données et quittait la piste. Le programme peut attendre ou ne pas attendre pour recevoir le transfert.
Parallélisme
Lorsque nous marquons une fonction comme "asynchrone" dans un logiciel, cela signifie souvent que nous voulons du parallélisme . Mais rappelez-vous que le parallélisme n'implique pas la synchronie . Les lucioles en sont un bon exemple, car elles ont également présenté un comportement synchrone et asynchrone. Alors que la plupart des mouches ont clignoté à l'unisson, beaucoup étaient manifestement en désaccord avec le reste du groupe et ont flashé plus au hasard. Les mouches agissaient peut-être simultanément , mais elles n'étaient pas toutes synchronisées .
Désormais, lorsque nous marquons du code "asynchrone", cela semble drôle, car cela implique que le reste du code non marqué est "sync". Qu'est ce que ça veut dire? N'avons-nous pas insisté sur le fait que la "synchronisation" nécessitait le tango à deux? Mais que se passe-t-il si nous parlons d’exécution de code dans un seul thread? Dans ce cas, nous devons prendre du recul et penser à un programme comme une séquence d'états et de transitions entre ces états. Une instruction dans un programme provoque une transition d'état. Nous pouvons penser à cela comme à un "micro-processus" qui commence et finit par la déclaration. Les points de séquence définis par le langage sont en fait les points de synchronisation de ces "micro-processus". Et ainsi, nous pouvons voir un seul thread,
L'intégrité du langage de programmation garantit que les mises à jour d'état n'interfèrent pas entre les instructions et les points de séquence définissent des limites entre lesquelles le compilateur n'est pas autorisé à effectuer des optimisations observables. Par exemple, l'ordre d'évaluation des expressions dans une instruction peut être indéfini ou sous-spécifié, ce qui donne au compilateur la liberté d'optimiser l'instruction de différentes manières. Mais au moment où l'instruction suivante commence, le programme doit être dans un état bien défini, si le PL est correct.
A présent, nous devrions clairement comprendre ce que nous entendons par "asynchrone". Cela signifie exactement que le contrat de synchronisation implicite dans un bloc de code est exempté pour le bloc asynchrone. Il est permis de mettre à jour l'état du programme indépendamment, sans les garanties de sécurité normalement inhérentes au modèle de calcul séquentiel (uniquement cohérent, synchrone). Bien entendu, cela signifie que nous devons faire particulièrement attention à ne pas détruire l'état du programme de manière incohérente. Cela signifie généralement que nous introduisons une synchronie explicite limitée pour assurer la coordination avec le bloc asynchrone. Notez que cela signifie que le bloc asynchrone peut être à la fois asynchrone et synchrone! Mais rappelant que la synchronisation indique simplement l’existence d’un point de synchronisation, nous ne devrions avoir aucune difficulté à accepter cette notion.