pour démarrer une boucle sans fin d'exécution de deux goroutines, je peux utiliser le code ci-dessous:
après avoir reçu le msg, il démarrera une nouvelle goroutine et continuera pour toujours.
c1 := make(chan string)
c2 := make(chan string)
go DoStuff(c1, 5)
go DoStuff(c2, 2)
for ; true; {
select {
case msg1 := <-c1:
fmt.Println("received ", msg1)
go DoStuff(c1, 1)
case msg2 := <-c2:
fmt.Println("received ", msg2)
go DoStuff(c2, 9)
}
}
Je voudrais maintenant avoir le même comportement pour N goroutines, mais à quoi ressemblera l'instruction select dans ce cas?
C'est le bit de code avec lequel j'ai commencé, mais je ne sais pas comment coder l'instruction select
numChans := 2
//I keep the channels in this slice, and want to "loop" over them in the select statemnt
var chans = [] chan string{}
for i:=0;i<numChans;i++{
tmp := make(chan string);
chans = append(chans, tmp);
go DoStuff(tmp, i + 1)
//How shall the select statment be coded for this case?
for ; true; {
select {
case msg1 := <-c1:
fmt.Println("received ", msg1)
go DoStuff(c1, 1)
case msg2 := <-c2:
fmt.Println("received ", msg2)
go DoStuff(c2, 9)
}
}
Réponses:
Vous pouvez le faire en utilisant la
Select
fonction du reflet package:Vous transmettez un tableau de
SelectCase
structures qui identifient le canal à sélectionner, le sens de l'opération et une valeur à envoyer dans le cas d'une opération d'envoi.Vous pouvez donc faire quelque chose comme ceci:
Vous pouvez expérimenter un exemple plus détaillé ici: http://play.golang.org/p/8zwvSk4kjx
la source
Vous pouvez accomplir cela en enveloppant chaque canal dans un goroutine qui "transfère" les messages vers un canal "agrégé" partagé. Par exemple:
Si vous avez besoin de savoir de quel canal provient le message, vous pouvez l'envelopper dans une structure avec des informations supplémentaires avant de le transmettre au canal agrégé.
Dans mes tests (limités), cette méthode surpasse largement les performances en utilisant le package Reflect:
Code de référence ici
la source
b.N
dans un benchmark. Sinon, les résultats (qui sont divisés parb.N
, 1 et 2000000000 dans votre sortie) n'auront aucun sens.reflect.Select
approche) est que les goroutines effectuent le tampon de fusion au minimum une valeur unique sur chaque canal fusionné. Habituellement, ce ne sera pas un problème, mais dans certaines applications spécifiques, cela peut être un facteur décisif :(.Pour développer certains commentaires sur les réponses précédentes et fournir une comparaison plus claire, voici un exemple des deux approches présentées jusqu'à présent avec la même entrée, une tranche de canaux à lire et une fonction à appeler pour chaque valeur qui doit également savoir laquelle canal d'où provient la valeur.
Il existe trois différences principales entre les approches:
Complexité. Bien que cela puisse être en partie une préférence du lecteur, je trouve l'approche du canal plus idiomatique, simple et lisible.
Performance. Sur mon système Xeon amd64, les canaux goroutines + exécutent la solution de réflexion d'environ deux ordres de grandeur (en général, la réflexion dans Go est souvent plus lente et ne doit être utilisée que lorsque cela est absolument nécessaire). Bien entendu, s'il y a un retard significatif dans la fonction traitant les résultats ou dans l'écriture des valeurs sur les canaux d'entrée, cette différence de performance peut facilement devenir insignifiante.
Sémantique de blocage / mise en mémoire tampon. L'importance de cela dépend du cas d'utilisation. Le plus souvent, cela n'a pas d'importance ou la légère mise en mémoire tampon supplémentaire dans la solution de fusion de goroutine peut être utile pour le débit. Cependant, s'il est souhaitable d'avoir la sémantique qu'un seul écrivain est débloqué et que sa valeur est entièrement gérée avant qu'un autre écrivain ne soit débloqué, cela ne peut être réalisé qu'avec la solution de réflexion.
Notez que les deux approches peuvent être simplifiées si "l'identifiant" du canal d'envoi n'est pas requis ou si les canaux source ne seront jamais fermés.
Canal de fusion Goroutine:
Réflexion sélectionnez:
[Code complet sur le terrain de jeu Go .]
la source
select
ou lereflect.Select
fait. Les goroutines continueront à tourner jusqu'à ce qu'elles consomment tout ce qui se trouve dans les canaux, il n'y a donc pas de moyen clair deProcess1
sortir tôt. Il y a aussi un risque de problèmes si vous avez plusieurs lecteurs, puisque les goroutines mettent en mémoire tampon un élément de chacun des canaux, ce qui ne se produira pas avecselect
.select
dans unefor
boucle au lieu de lafor range
boucle plus simple actuellement utilisée. Process2 aurait besoin de coller un autre cascases
et de gérer spécialement cette valeur dei
.Pourquoi cette approche ne fonctionnerait-elle pas en supposant que quelqu'un envoie des événements?
la source
select
sur plusieurs canaux (sansdefault
clause) est qu'il attend efficacement qu'au moins un soit prêt sans tourner.Option éventuellement plus simple:
Au lieu d'avoir un tableau de canaux, pourquoi ne pas passer un seul canal en tant que paramètre aux fonctions exécutées sur des goroutines séparées, puis écouter le canal dans un goroutine consommateur?
Cela vous permet de sélectionner un seul canal dans votre auditeur, ce qui permet une sélection simple et d'éviter la création de nouvelles goroutines pour regrouper les messages de plusieurs canaux?
la source