Récemment, je regardais la programmation simultanée dans Go, et j'ai découvert que tout dépendait de cette partie du contenu, j'ai donc dû serrer les dents, mais vous constaterez que c'est ce n'est pas si difficile après l'avoir regardé.
Parfois, vous pouvez ranger des choses que vous ne voulez pas voir, puis les vérifier après avoir concentré votre attention. Vous obtiendrez des gains inattendus.
L'article d'aujourd'hui est une explication simple. Kaka s'est également inscrit à un cours de go. Je verrai si je peux mieux comprendre dans quel cours, puis je ferai des suppléments approfondis.
- Ajoutez simplement go avant la fonction
- Il n'est pas nécessaire de distinguer s'il s'agit d'une fonction asynchrone dans la définition
- Le planificateur fera l'affaire au point approprié. Il existe de nombreux points de commutation. Ceci n'est qu'une référence. Il n'y a aucune garantie de commutation ou qu'il ne sera pas commuté à d'autres endroits. Opérations IO, canaux, attente de verrous, appels de fonction, runtime.Gosched(), etc. . .
- Utilisez race pour détecter les conflits d'accès aux données
Lisez d'abord le cas pour savoir comment utiliser goroutine
Ce cas est un simple code d'exécution simultanée, qui n'est que le mot-clé go in go.
Jetons donc un coup d'œil à ce que ce code va générer
D'après l'image ci-dessus, vous pouvez voir que cette ligne de code ne génère rien et se termine directement. Alors, que se passe-t-il exactement ?
La raison pour laquelle nous quittons directement est que les impressions main et fmt dans notre code sont exécutées simultanément. Si fmt imprime les données de toute urgence avant qu'elles n'arrivent, la boucle externe est déjà terminée, puis se termine directement.
Dans le langage go ! Supposons qu'après la sortie d'une fonction principale, elle tuera directement toutes les goroutines, le phénomène est donc que les données d'impression urgentes sont renvoyées avant l'arrivée de la goroutine.
Alors vous vous demandez, comment pouvez-vous voir les données imprimées ? En fait, c'est très simple, il suffit de ne pas quitter précipitamment après l'exécution de la fonction principale et de lui laisser un peu de temps pour attendre. Regardez le cas
Le résultat souhaité cette fois est affiché.
Dans ce cas, le nombre de goroutines ouvertes est de 10, alors que se passera-t-il s'il passe à 1000 ?
Les résultats s'affichent toujours normalement, tout comme 1 000 personnes imprimant des choses en même temps.
Alors, qu'est-ce que le réglage 10 a à voir avec 1000 ?
Ceux qui connaissent le système d'exploitation doivent savoir qu'il n'y a aucun problème à ouvrir 10 threads, et qu'il n'y a pas de gros problème à ouvrir 100 threads, mais c'est presque suffisant.
Généralement, il suffit d'ouvrir des dizaines de threads dans le système, mais si 1 000 personnes doivent faire une chose en même temps, les threads ne peuvent pas être utilisés pour résoudre le problème et une méthode asynchrone est requise.
Mais en langue go ! Utilisez simplement le mot-clé go directement et il peut être exécuté simultanément.
Ensuite, parlons des raisons pour lesquelles Go peut imprimer 1 000 personnes en même temps.
Tout d'abord, jetons un coup d'œil à la différence entre les coroutines et les threads.
Coroutine peut être comprise commeFil léger
,Multitâche non préemptif, la coroutine cède activement le contrôle
.轻量级的线程
,非抢占式多任务处理,由协程主动交出控制权
。
线程大家应该都知道是可以被操作系统在任何时候进行切换,所以说线程就是抢占式多任务处理,线程是没有控制权,哪怕是一个语句执行到一半都会被操作系统切掉,然后转到其它线程去操作。
那么反之对于协程来说,什么时候交出控制权,什么时候不交出控制权是由协程内部主动决定的,正是因为这种非抢占式,所以被称之为轻量级。
Tout le monde doit savoir que les threads peuvent être commutés par le système d'exploitation à tout moment, les threads sont donc multitâches préemptifs. Même si une instruction est exécutée à mi-chemin, elle sera coupée par le système d'exploitation. allez sur d'autres threads pour fonctionner.
À l'inverse, pour la coroutine, quand céder les droits de contrôle et quand ne pas céder les droits de contrôle sont activement décidés par la coroutine C'est précisément à cause de ce style non préemptif qu'on l'appelle magnitude légère.
et
Plusieurs coroutines peuvent s'exécuter sur un ou plusieurs threads
. 2. chaîne
Dans la première section, nous avons appris que vous pouvez ouvrir beaucoup de goroutines en go, puis le canal bidirectionnel entre les goroutines est le canal
Comme vous pouvez le voir dans le cas ci-dessus, vous pouvez directement utiliser la fonction make pour créer une chaîne.
Les septième et huitième lignes servent à envoyer des données au canal.
Alors cette affaire peut-elle être menée ? Venez l'essayer
Vous pouvez voir qu'une erreur a été signalée à ce moment. L'erreur signifie qu'un blocage se produira lors de l'envoi de 1 au canal.
Retournez ensuite à la photo précédente.
Comme nous l'avons dit plus haut, le canal est une interaction entre goroutine et goroutine.
Mais dans ce cas, il n'y a qu'une seule goroutine, il faut donc une autre goroutine pour la recevoir.
Maintenant, vous devriez savoir comment démarrer une goroutine.
Dans l'image ci-dessus, nous avons récemment ouvert une autre goroutine, puis utilisé une boucle infinie pour recevoir la valeur envoyée par le canal et l'imprimer.
Mais vous constaterez que nous avons envoyé deux données au canal, mais le résultat de l'impression à ce moment ne contient qu'une seule donnée. Mais c’est mieux que ce avec quoi nous avons commencé, n’est-ce pas !
Alors pourquoi cela arrive-t-il ?
Vous pouvez comprendre le flux d'exécution du code. Tout d'abord, un 1 est envoyé au canal, puis la première valeur est obtenue dans une boucle et imprimée.
Envoyez à nouveau les données 2 au canal, mais quittez directement avant l'impression, ce qui entraîne le phénomène selon lequel seules les données 1 sont affichées mais pas les données 2.
Vous devriez déjà savoir comment résoudre ce problème grâce à la description de Kaka.
Il s'agit d'ajouter un temps de sortie retardé à la fonction ChannelDome.
Passer le canal en paramètre
Comme vous pouvez le voir ci-dessus, go est suivi d'une fonction de fermeture, et le c utilisé dans cette fermeture est la couche externe utilisée c.
Alors est-il possible de passer ce c en utilisant des paramètres ? La réponse est oui.
Bien sûr, vous pouvez également transmettre d'autres paramètres
Vous pouvez voir sur l'image ci-dessus que non seulement le canal mais aussi le paramètre id sont transmis. En même temps, le code peut également être directement optimisé dans la partie ci-jointe, c'est-à-dire que la valeur est. pris directement de la chaîne.
Comme vous pouvez le voir sur l'image ci-dessus, chacun a sa propre chaîne, puis la distribue après distribution, chacun recevra sa propre valeur reçue et l'imprimera. ça sort.
De même, vous pouvez voir que nous avons ajouté une nouvelle boucle for à la ligne 26 pour envoyer des données au canal.
D'après les résultats en cours, vous constaterez que l'ordre d'impression est déroutant, comme par exemple les deux valeursreçois i et reçois I.
À ce stade, aurez-vous des doutes ? Lorsque nous envoyons des données à la chaîne, nous les envoyons dans l'ordre ! Ensuite, ils doivent être reçus dans l'ordre lors de la réception.
Comme nous sommes très sûrs que les données sont envoyées dans l'ordre, le problème ne peut survenir qu'avec Printf.
Parce que Printf a IO et est planifié par goroutine, alors Printf à ce moment est en panne, mais les valeurs reçues seront imprimées une par une.
Utiliser le canal comme valeur de retour
Les cas des sections précédentes ont tous été créés par canal puis transmis en tant que paramètres.
Ensuite, cette section renverra le canal comme valeur de retour.
package mainimport ( "fmt" "time")func createWorker(id int) chan int { c := make(chan int) go func() { for { fmt.Printf("Worker %d receive %c\n", id,
Copier après la connexion
De là, vous pouvez voir que nous avons changé la fonction worker en fonction createWorker, car dans cette fonction le canal est créé directement.
Ensuite, la valeur reçue par le canal est imprimée via une coroutine.
Jetons un œil aux résultats en cours d'exécution
Vous pouvez savoir grâce aux résultats en cours d'exécution que notre écriture de code est toujours correcte, mais vous pouvez voir comment utiliser le canal renvoyé à ce moment de manière très intuitive
Mais s'il y a beaucoup de codes, vous ne savez pas comment utiliser ce canal du tout Utilisé, tout ce code doit être simplement modifié.
Ensuite, ce qu'il faut faire, c'est dire aux gens de l'extérieur comment l'utiliser.
Vous pouvez savoir à partir du code ci-dessus que les données sont envoyées au canal, donc le canal renvoyé par la méthodecreateWorker
doit être marqué
Le code actuel ressemble donc à ceci. Nous marquons directement la direction du canal de valeur de retour de la méthode createWorker. La fonction est d'envoyer des données.
Ensuite, lors de l'impression, c'est le reçu, qui semble très intuitif.
Après avoir modifié les deux étapes ci-dessus, vous constaterezcreateWorker
调用是报错了,Cannot use 'createWorker(i)' (type chanLorsque vous voyez l'erreur, sachez que les deux types ne sont pas équivalents.
Après modification, vous constaterez que la compilation est correcte et aucun message d'erreur n'est signalé.
Parlons de la programmation simultanée dans Go (1)也是ok的。
package mainimport ( "fmt" "time")func createWorker(id int) chan
Copier après la connexion
学习了这么久了,那么咔咔问你一个问题,这段代码执行会发生什么?
Oui, une erreur se produira, car comme mentionné au début de l'article, pour envoyer des données à un canal, vous devez ouvrir une autre coroutine pour recevoir les données.
Bien que les coroutines soient considérées comme légères, après l'envoi de données, vous devez changer de coroutines pour recevoir des données, ce qui nécessite beaucoup de ressources.
Alors c’est ce que je vais vous expliquer dans cette section.
Créez un canal pouvant avoir 3 tampons, puis envoyez 3 données au canal.
Vous pouvez également savoir grâce aux résultats de la course à pied en même temps que cela ne se produit pasdeadlock
.
Une question pour vous, que se passera-t-il si vous envoyez une donnée 4 au tampon ?
Vous êtes intelligent, vous devez avoir pensé au résultat, oui, l'avez signalédeadlock
Ensuite, nous utiliserons le travailleur précédent pour recevoir les données du canal.
Mais vous constaterez que le résultat en cours d'exécution n'imprime toujours pas le 1,2,3,4 envoyé.
Cette question a été posée plusieurs fois maintenant. Vous pouvez essayer de vous demander comment résoudre cette situation.
Ajoutez simplement un temps de retard. Au fait, laissez-moi vous expliquer que le cas précédent imprimait les lettres et tout le formatage %c, maintenant il imprime les chiffres, donc changez Pour %d, un petit changement. .
L'établissement de canaux de cette manière a un certain effet sur l'amélioration des performances.
Avez-vous découvert un problème jusqu'à présent, c'est-à-dire que vous ne savez pas quand la chaîne a été envoyée ?
Examinons ensuite ce problème.
Empruntez le code du cas précédent pour continuer l'explication.
Ce qui est incohérent avec le code précédent, c'est que nous avons ajouté close à la fin. Il est à noter que close est fermé sur l'expéditeur.
Vous verrez que les résultats en cours ne sont pas satisfaisants, vous constaterez que même si 1,2,3,4 sont reçus.
Mais de nombreux 0 ont été reçus ci-dessous, mais la capture d'écran n'a capturé qu'une seule donnée.
Bien que l'expéditeur ferme le canal, le travailleur recevra toujours les données lorsque le canal sera fermé. Cela ne signifie pas que les données ne seront pas reçues après la fermeture du canal.
Mais lorsque l'expéditeur définit la fermeture de canal, les données reçues sont toutes 0, c'est-à-dire que la valeur du paramètre c chan int transmis par la méthode de travail est 0.
Maintenant, notre chaîne est de type int, et nous avons reçu 0. Ensuite, s’il s’agit d’un type chaîne, ce qui est reçu est une chaîne vide.
Combien de temps cela prendra-t-il ? C'est le temps d'une milliseconde que nous avons fixé.
Si on vous demandait de changer ce programme, avez-vous des idées ? Si vous n’en avez aucune idée, balancez-vous simplement au rythme de ce clic.
Dans la fonction worker, deux valeurs sont utilisées pour recevoir, n est le canal c transmis. ok est de déterminer si la valeur existe.
Vous pouvez voir les résultats en cours et vous ne recevrez plus 0 données.
En plus de cette façon d'écrire, il existe une manière plus simple.
La persévérance dans l'apprentissage, la persévérance dans l'écriture et la persévérance dans le partage sont les convictions auxquelles Kaka a toujours adhéré depuis ses débuts. J'espère que les articles de Kaka sur le grand Internet pourront vous apporter un peu d'aide. Je m'appelle Kaka, à la prochaine fois.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!