Comprenez parfaitement ce que sont synchrone et asynchrone dans un seul article !

Je crois que lorsque de nombreux étudiants rencontrent les deux mots synchrone et asynchrone, leur cerveau tombe instantanément dans un état de confusion comme un carrefour où le feu de circulation tombe en panne :

Oui, ces deux mots qui se ressemblent beaucoup mais qui sont en réalité très similaires ont causé bien des ennuis aux blogueurs. Quelle est la signification derrière ces deux mots ?

Commençons par la scène du travail.

Programmeur travailleur

Supposons maintenant que votre patron vous ait assigné une tâche très urgente et importante que vous devez accomplir avant de quitter le travail (capitalisme maléfique). Afin de superviser les progrès, le patron a déplacé une chaise et s'est assis à l'écart pour vous regarder écrire du code. Vous avez dû jurer dans votre esprit : " WTF, as-tu autant de temps libre ? Tu ne peux pas faire autre chose en me regardant ? " Le patron semblait avoir reçu vos ondes cérébrales : " J'attends juste ici ... Je ne vais nulle part, pas même aux toilettes, jusqu'à ce que tu aies fini d'écrire.

Dans cet exemple, le patron continue d'attendre après vous avoir confié la tâche et ne fait rien jusqu'à ce que vous ayez fini d'écrire . Ce scénario est appelé synchronisation. Le lendemain, votre patron vous confie une autre tâche. Mais cette fois, je n'étais pas si anxieux. Cette fois, le patron a dit à la légère : " Jeune homme, oui, pas mal. Si vous travaillez dur pendant encore un an, je serai libre financièrement l'année prochaine. Il n'y a pas d'urgence pour la tâche d'aujourd'hui. Juste fais-moi savoir quand tu auras fini de l'écrire." ". Cette fois, le patron ne vous a pas regardé écrire du code, mais s'est retourné pour regarder la vidéo. Une fois que vous avez fini d'écrire, vous avez simplement signalé au patron "J'ai fini".

Dans cet exemple, une fois que le patron a terminé la tâche, il n'attend plus et ne fait rien mais va travailler sur autre chose. Une fois la tâche terminée, vous dites simplement au patron que la tâche est terminée. C'est ce qu'on appelle asynchrone. Il convient de noter que dans un scénario asynchrone, l'accent est mis sur le fait que votre patron regarde l'émission pendant que vous écrivez du code. Les deux choses se produisent en même temps , plutôt que l'une des parties attend l'autre , c'est pourquoi le scénario asynchrone Généralement plus efficace que synchrone. L'essentiel est que , quel que soit le scénario dans lequel les applications synchrones et asynchrones sont utilisées. On voit que le mot synchronisation est souvent lié aux mots-clés tels que "dépendance", "associé" et "en attente" de la tâche, tandis que asynchrone est souvent lié aux mots-clés de la tâche tels que "non dépendant", " sans rapport", "pas besoin d'attendre" et "simultanément". Mots clés associés tels que "occurrence". À propos, si vous rencontrez un patron qui vous regarde écrire du code par derrière, trente-six stratégies sont la meilleure stratégie.

Passer des appels téléphoniques et envoyer des e-mails

En tant que programmeur assidu, vous ne pouvez pas simplement vous immerger dans les briques de construction. La communication dans le travail quotidien ne peut être évitée. L'un des moyens de communication les plus efficaces est la querelle. . . Ah non, c'est le téléphone.

Habituellement, lorsque vous passez un appel téléphonique, une personne parle et l'autre écoute. Pendant qu'une personne parle, l'autre personne attend , puis continue une fois que l'autre personne a fini de parler. Par conséquent, dans cette scène, vous pouvez voir que "dépendance" Des mots clés tels que "corrélation" et "attente" apparaissent, de sorte que la méthode de communication pour passer des appels téléphoniques est ce qu'on appelle la synchronisation.

Le courrier électronique est une autre méthode de communication courante parmi les codeurs. Le courrier électronique est un autre moyen de communication essentiel, car personne n'attend que vous écriviez un courrier électronique sans rien faire, vous pouvez donc écrire lentement et tranquillement, et pendant que vous écrivez le courrier électronique, le destinataire peut faire quelque chose comme pêcher. les toilettes, et en même temps se plaignent de choses significatives comme pourquoi il n'y a pas deux semaines de vacances pour la fête nationale. En même temps, une fois que vous avez fini de rédiger l'e-mail et de l'envoyer, vous n'avez pas besoin d'attendre que l'autre partie réponde et ne fasse rien. Vous pouvez également faire des choses significatives comme pêcher.

Ici, vous écrivez un email et d'autres en profitent. Les deux choses se passent en même temps. Ni le destinataire ni l'expéditeur n'ont besoin de s'attendre. Lorsque l'expéditeur a fini d'écrire l'e-mail, il peut simplement cliquer sur envoyer L'expéditeur peut le lire après l'avoir reçu. Le destinataire et l'expéditeur n'ont pas besoin de compter l'un sur l'autre ou de s'attendre l'un l'autre. Vous voyez, dans ce scénario, les mots-clés « non dépendant », « sans rapport » et « pas besoin d'attendre » apparaissent, la méthode de communication par courrier électronique est donc asynchrone.

Appel synchrone

Revenons enfin au sujet de la programmation. Maintenant que nous avons compris la signification de la synchronisation et de l'asynchrone dans divers scénarios (je l'espère), comment les programmeurs devraient-ils comprendre la synchronisation et l'asynchrone ? Parlons d’abord des appels synchrones, qui sont le scénario le plus familier aux programmeurs. Les appels de fonctions générales sont synchrones, comme ceci :

funcA() {
// 等待函数funcB执行完成
    funcB();

// 继续接下来的流程
}

funcA appelle funcB, puis le code suivant dans funcA ne sera pas exécuté tant que funcB n'aura pas terminé l'exécution, ce qui signifie que funcA doit attendre que funcB termine son exécution, comme ceci :

D'après l'image ci-dessus, nous pouvons voir que funcA ne peut rien faire pendant que funcB est en cours d'exécution. Il s'agit d'une synchronisation typique.

Notez que d'une manière générale, pour les appels synchrones comme celui-ci, funcA et funcB s'exécutent dans le même thread, ce qui est la situation la plus courante. Mais il convient de noter que même les fonctions exécutées dans deux threads différents peuvent être appelées de manière synchrone. Lorsque nous effectuons des opérations d'E/S, la couche inférieure envoie en fait des requêtes au système d'exploitation via des appels système, tels que la lecture de fichiers disque :

read(file, buf);

Cela bloque les E/S. Le programme ne peut pas continuer à avancer avant le retour de la fonction de lecture.

read(file, buf);
// 程序暂停运行,
// 等待文件读取完成后继续运行

comme le montre la photo :

Ce n'est qu'après le retour de la fonction de lecture que le programme peut continuer son exécution. Notez que, contrairement à l'appel synchrone ci-dessus, la fonction et la fonction appelée s'exécutent dans des threads différents. Par conséquent, nous pouvons conclure que les appels synchrones n'ont rien à voir avec le fait que la fonction et la fonction appelée s'exécutent dans le même thread . Ici, nous tenons à souligner à nouveau que la fonction et la fonction appelée ne peuvent pas être exécutées en même temps en mode synchrone. La programmation synchrone est la plus naturelle et la plus facile à comprendre pour les programmeurs. Mais le prix, facile à comprendre, est que dans certains scénarios, la synchronisation n'est pas efficace, pour la raison simple, car les tâches ne peuvent pas être effectuées en même temps . Nous examinons ensuite les appels asynchrones.

   Information Direct : parcours d'apprentissage de la technologie du code source du noyau Linux + didacticiel vidéo sur le code source du noyau

Learning Express : code source du noyau Linux, réglage de la mémoire, système de fichiers, gestion des processus, pilote de périphérique/pile de protocole réseau

appel asynchrone

Il existe des appels synchrones et des appels asynchrones. Si vous comprenez vraiment ce que j'ai lu dans cette section jusqu'à présent, les appels asynchrones ne seront pas un problème pour vous. D'une manière générale, les appels asynchrones vont toujours de pair avec des tâches chronophages telles que les opérations d'E/S , telles que la lecture et l'écriture de fichiers disque, l'envoi et la réception de données réseau, les opérations de base de données, etc. Prenons toujours comme exemple la lecture de fichiers disque. Dans le mode d'appel synchrone de la fonction de lecture, l'appelant ne peut pas continuer à avancer avant la lecture du fichier, mais la situation est différente si la fonction de lecture peut être appelée de manière asynchrone. Si la fonction read peut être appelée de manière asynchrone, la fonction read peut revenir immédiatement même si le fichier n'a pas été lu.

read(file, buff);
// read函数立即返回
// 不会阻塞当前程序

comme ça:

On peut voir que dans la méthode d'appel asynchrone, l'appelant ne sera pas bloqué et le programme suivant pourra être exécuté immédiatement après la fin de l'appel de fonction. Le point clé de l'asynchrone à ce moment-là est que l'exécution ultérieure du programme par l'appelant peut être effectuée en même temps que la lecture du fichier.Nous pouvons également le voir dans la figure ci - dessus.C'est l'efficacité de l'asynchrone. Cependant, veuillez noter que les appels asynchrones sont un fardeau pour les programmeurs en termes de compréhension, et encore plus un fardeau pour l'écriture de code. En général, lorsque Dieu vous ouvre une porte, il la fermera de manière appropriée. Certains étudiants peuvent demander que, dans le cadre d'un appel synchrone, l'appelant ne continue plus l'exécution mais fait une pause et attend. Une fois la fonction appelée exécutée, il est naturel que l'appelant continue à s'exécuter. Ainsi, dans le cadre d'un appel asynchrone, comment l'appelant sait-il si la fonction appelée est L'exécution est-elle terminée ? Ceci se divise en deux situations :

  1. L'appelant ne se soucie pas du tout des résultats de l'exécution
  2. L'appelant doit connaître le résultat de l'exécution

Le premier cas est relativement simple et ne nécessite aucune discussion. Le deuxième cas est plus intéressant. Il existe généralement deux méthodes d'implémentation : l'une est le mécanisme de notification, ce qui signifie que lorsque l'exécution de la tâche est terminée, un signal est envoyé pour avertir l'appelant que la tâche est terminée. Notez qu'il existe de nombreuses implémentations. méthodes pour le signal ici. Signal Linux dans , ou en utilisant des mécanismes tels que des sémaphores. L'autre est le rappel, qui est ce que nous appelons souvent le rappel. Nous nous concentrerons sur le rappel dans le prochain article, et il y aura une brève discussion dans cet article. Ensuite, nous utilisons un exemple spécifique pour expliquer les appels synchrones et les appels asynchrones.

Synchrone VS Asynchrone

Nous prenons comme exemple un service Web commun pour illustrer ce problème. D'une manière générale, le serveur Web aura une logique de traitement typique après réception de la demande de l'utilisateur. La plus courante est la requête de base de données (bien sûr, vous pouvez également remplacer la requête de base de données ici par d'autres opérations d'E/S, telles que la lecture de disque et le réseau). communication. etc.), nous supposons ici que le traitement d'une requête utilisateur nécessite de passer par les étapes A, B et C, puis de lire la base de données. Une fois la lecture de la base de données terminée, elle doit passer par les étapes D, E et F. , comme ça:

# 处理一次用户请求需要经过的步骤:
A;
B;
C;
数据库读取;
D;
E;
F;

Les étapes A, B, C et D, E, F ne nécessitent aucune E/S, ce qui signifie que ces six étapes ne nécessitent pas de lecture de fichiers, de communications réseau, etc. Seule l'étape de requête de base de données implique des opérations d'E/S. D'une manière générale, un tel serveur Web a deux threads typiques : le thread principal et le thread de traitement de la base de données. Notez que cette discussion n'est qu'un scénario typique. L'activité spécifique peut en fait être différente, mais cela n'affecte pas notre utilisation de deux threads. pour illustrer le problème. Examinons d’abord l’implémentation la plus simple, à savoir la synchronisation. Cette méthode est la plus naturelle et la plus simple à comprendre :

// 主线程
main_thread() {
    A;
    B;
    C;
    发送数据库查询请求;
    D;
    E;
    F;
}
// 数据库线程
DataBase_thread() {
while(1) {
        处理数据库读取请求;
        返回结果;
    }
}

Il s'agit de la méthode de synchronisation la plus courante. Après avoir émis une requête de requête de base de données, le thread principal sera bloqué et suspendu jusqu'à ce que la requête de base de données soit terminée. D, E et F peuvent continuer à s'exécuter, comme ceci :

Sur l'image, nous pouvons voir qu'il y aura un "espace" dans le thread principal. Cet espace est le "temps libre" du thread principal. Pendant ce temps libre, le thread principal doit attendre la fin de la base de données. requête avant de poursuivre le processus de traitement ultérieur. Ici, le fil principal est comme le patron qui supervise le travail, et le fil de base de données est comme le programmeur qui travaille dur pour déplacer les briques. Le patron ne fait rien jusqu'à ce que les briques soient déplacées, il vous regarde simplement de près et attend que vous le fassiez. finir de déplacer les briques avant de passer à autre chose. Évidemment, les programmeurs efficaces ne peuvent pas tolérer que le thread principal soit paresseux . Il est temps d’utiliser la grande arme, qui est asynchrone. Dans le schéma d'implémentation asynchrone, le thread principal n'attend pas du tout que la requête de base de données soit terminée, mais traite directement la requête suivante après avoir envoyé la requête de lecture et d'écriture de la base de données. Certains étudiants peuvent avoir des questions. Une requête doit passer par sept étapes : A, B, C, requête de base de données, D, E et F. Si le thread principal traite directement l'étape suivante après avoir terminé A, B, C et la base de données requête., qu'en est-il des étapes restantes D, E et F de la requête précédente ? Si vous n'avez pas oublié le contenu de la section précédente, sachez qu'il existe deux situations, discutons-en séparément.

1.Le thread principal ne se soucie pas des résultats de l'opération de la base de données.Dans ce cas, le thread principal ne se soucie pas du tout de savoir si la requête de base de données est terminée.Une fois la requête de base de données terminée, il traitera les trois étapes suivantesD,E , et F seul, comme ceci :

Vous voyez, voici le point clé.

Nous avons dit qu'une requête doit passer par sept étapes, dont les trois premières sont complétées dans le thread principal et les quatre dernières dans le thread de la base de données. Alors, comment le thread de la base de données sait-il traiter D, E, Que sont ces étapes ? À ce moment-là, notre autre fonction de rappel du protagoniste commence à apparaître. Oui, la fonction de rappel est utilisée pour résoudre ce problème. Nous pouvons encapsuler les étapes de traitement de D, E et F dans une fonction. Supposons que la fonction s'appelle handle_DEF_after_DB_query :

void handle_DEF_after_DB_query () {
    D;
    E;
    F;
}

De cette façon, le thread principal passera la fonction en paramètre lors de l'envoi de la requête de requête à la base de données :

DB_query(request, handle_DEF_after_DB_query);

Une fois le traitement du thread de base de données terminé, appelez simplement handle_DEF_after_DB_query directement. C'est le rôle de la fonction de rappel. Certains étudiants peuvent également se poser des questions : pourquoi cette fonction devrait-elle être transmise au thread de base de données au lieu d'être définie et appelée par le thread de base de données lui-même ? Parce que du point de vue de la structure organisationnelle du logiciel, ce n'est pas le travail que le thread de base de données devrait faire . Tout ce que le thread de la base de données doit faire est d'interroger la base de données, puis d'appeler une fonction de traitement. Quant à ce que fait cette fonction de traitement, le thread de la base de données ne s'en soucie pas du tout, et il ne devrait pas s'en soucier . Vous pouvez transmettre diverses fonctions de rappel. En d'autres termes, le système de base de données peut être programmé pour que la variable de fonction abstraite de la fonction de rappel puisse mieux faire face aux changements, car la modification du contenu de la fonction de rappel n'affectera pas la logique du thread de base de données, et si le thread de base de données définit sa propre fonction de traitement, alors il n'y a aucune flexibilité dans cette conception. Du point de vue du développement logiciel, en supposant que la logique des threads de base de données est encapsulée et fournie à d'autres équipes pour les bibliothèques, comment l'équipe de base de données peut-elle savoir quoi faire après une requête de base de données lors du développement ? Évidemment, seul l'utilisateur sait quoi faire après avoir interrogé la base de données, il peut donc simplement transmettre cette fonction de rappel lors de son utilisation. De cette façon, l'équipe de base de données complexe réalise ce qu'on appelle le découplage de l'équipe d'utilisateurs . Vous devez maintenant comprendre le rôle de la fonction de rappel.

Regardez également attentivement les deux images ci-dessus. Pouvez-vous comprendre pourquoi l'asynchrone est plus efficace que le synchrone ? La raison est très simple, c'est ce que nous avons évoqué dans cet article : l'asynchronie ne nécessite naturellement aucune attente ni dépendance. De l'image précédente, nous pouvons voir que le "temps libre" du thread principal a disparu, remplacé par un travail constant, du travail, du travail, tout comme les programmeurs 996 qui travaillent dur, et le thread de base de données n'est pas si long. est remplacé par le travail, le travail, le travail.

Les demandes de traitement du thread principal et les demandes de requête de traitement de la base de données peuvent être exécutées en même temps.Par conséquent, du point de vue des performances du système, cette conception permet une utilisation plus complète des ressources du système et un traitement plus rapide des demandes ; du point de vue de l'utilisateur, le système la réponse sera également plus rapide. . C'est l'efficacité de l'asynchrone. Mais nous devrions également pouvoir voir que la programmation asynchrone n'est pas aussi facile à comprendre que le mode synchrone et que la maintenabilité du système n'est pas aussi bonne que le mode synchrone. Existe-t-il alors un moyen de combiner la facilité de compréhension du mode synchrone avec l’efficacité du mode asynchrone ? La réponse est oui, et nous expliquerons cette technologie en détail dans les chapitres suivants. Examinons ensuite la deuxième situation, c'est-à-dire que le thread principal doit se soucier des résultats de la requête de base de données.

2. Le thread principal se soucie des résultats des opérations de la base de données .Dans ce cas, le thread de la base de données doit envoyer les résultats de la requête au thread principal en utilisant le mécanisme de notification.Après avoir reçu le message, le thread principal continue de traiter la seconde moitié du demande précédente, comme ceci :

De là, nous pouvons voir que les étapes de ABCDEF sont toutes traitées dans le thread principal. En même temps, le thread principal n'a pas non plus de "temps libre", mais dans ce cas, le thread de la base de données est relativement tranquille. Il n'y a pas de méthode précédente ici. La méthode est efficace, mais elle reste plus efficace que le mode synchrone. Enfin, il convient de noter que l'asynchrone n'est pas nécessairement plus efficace que le synchrone dans tous les cas, mais qu'il doit également être analysé en fonction des spécificités métier et de la complexité des IO.

Résumer

Dans cet article, nous analysons les deux concepts de synchronisation et d'asynchronisme à partir de différents scénarios, mais quel que soit le scénario, la synchronisation signifie souvent que les deux parties doivent s'attendre l'une l'autre et dépendre l'une de l'autre, tandis qu'asynchrone signifie que les deux parties sont indépendantes. les uns des autres et faire leur propre truc.

Auteur original : Coder's Desert Island Survival

Je suppose que tu aimes

Origine blog.csdn.net/youzhangjing_/article/details/132739158
conseillé
Classement