Golang: structure anonyme et structure vide
http://play.golang.org/p/vhaKi5uVmm
package main
import "fmt"
var battle = make(chan string)
func warrior(name string, done chan struct{}) {
select {
case opponent := <-battle:
fmt.Printf("%s beat %sn", name, opponent)
case battle <- name:
// I lost :-(
}
done <- struct{}{}
}
func main() {
done := make(chan struct{})
langs := []string{"Go", "C", "C++", "Java", "Perl", "Python"}
for _, l := range langs { go warrior(l, done) }
for _ = range langs { <-done }
}
[1re Question]
done <- struct{}{}
Comment et Pourquoi avons-nous besoin de cette étrange structure? Est-ce une structure vide ou anonyme? J'ai cherché sur Google mais je n'ai pas trouvé la bonne réponse ou la bonne documentation pour expliquer cela.
La source d'origine est de Andrew Gerrand de l' parler http://nf.wh3rd.net/10things/#10
Ici
make(chan struct{})
done est un canal de type struct{}
alors j'ai essayé avec
done <- struct{}
Mais ça ne fonctionne pas. Pourquoi ai-je besoin de crochets supplémentaires pour cette ligne?
done <- struct{}{}
[2e Question]
for _ = range langs { <-done }
Pourquoi ai-je besoin de cette ligne? Je sais que cette ligne est nécessaire parce que, sans cette ligne, pas de sortie. Mais pourquoi et quoi cette ligne? Et qu'est-ce qui le rend nécessaire dans ce code? Je sais que <-done
est à recevoir valeurs à partir du canal de fait et de jeter les valeurs reçues. Mais pourquoi ai-je besoin de faire cela?
Merci!
5 réponses
les littérales composites construisent des valeurs pour les structures, les tableaux, les tranches et créer une nouvelle valeur chaque fois qu'ils sont évalués. Ils se composent du type de la valeur suivie d'une liste à brace-bound de composite élément. Un élément peut être une expression unique ou une paire clé-valeur.
struct{}{}
est un composite littéral de type struct{}
, le type de la valeur suivie d'une liste de des éléments composites.
for _ = range langs { <-done }
attend jusqu'à ce que tous les goroutines pour tous les langs
avez envoyé done
messages.
notez qu'un aspect intéressant de l'utilisation de struct{} pour le type poussé sur un canal (par opposition à int ou bool), est que le taille d'une structure vide... 0!
Voir l'article récent "la structure vide " (mars 2014) by Dave Cheney.
vous pouvez en créer autant struct{}
comme vous le souhaitez (struct{}{}
) pour les pousser sur votre Canal: votre mémoire ne sera pas affectée.
Mais vous pouvez l'utiliser pour signaler entre aller routines, comme illustré dans "Curieux Canaux".
et vous conservez tous les autres avantages liés à une structure:
- vous pouvez définir des méthodes (type peut être une méthode récepteur)
- vous pouvez implémenter une interface (avec les méthodes que vous venez de définir sur votre struct vide)
- un singleton
dans Go vous pouvez utiliser une structure vide, et stocker toutes vos données dans les variables globales. Il n'y aura qu'une seule instance de ce type, puisque toutes les structures sont interchangeables.
Voir par exemple le global var errServerKeyExchange
dans le fichier vide struct rsaKeyAgreement
est défini.
struct{}
est un type (en particulier, une structure sans membres). Si vous avez un typeFoo
, vous pouvez créer une valeur de ce type dans une expression avecFoo{field values, ...}
. Mettre cela ensemble,struct{}{}
est une valeur de typestruct{}
, ce que la chaîne attend.main
fonction engendrewarrior
goroutines, qui écriront à ladone
canaliser quand ils ont terminé. Le dernierfor
bloc lit sur ce canal, veiller à ce quemain
ne reviendra pas tant que les goroutines n'auront pas fini. Ceci est important parce que le programme se terminera quandmain
complète, qu'il y ait ou non d'autres goroutines en cours d'exécution.
les Bonnes questions,
le point entier du canal de struct dans ce scénario est simplement de signaler l'achèvement que quelque chose d'utile est arrivé. Le type de canal n'a pas vraiment d'importance, il aurait pu utiliser un int ou un bool pour accomplir le même effet. Ce qui est important, c'est que son code s'exécute de façon synchronisée où il fait la comptabilité nécessaire pour signaler et passer à des points clés.
j'accepte la syntaxe de struct{}{}
semble étrange au premier abord parce que dans cet exemple il déclare une structure et la crée en ligne d'où le deuxième ensemble de crochets.
si vous aviez un objet préexistant comme:
type Book struct{
}
Vous pourriez créer comme ceci: b := Book{}
, vous n'avez besoin que d'un seul jeu de crochets car la structure du livre a déjà été déclarée.
done
le canal est utilisé pour recevoir des notifications de warrior
méthode qui indique que le travailleur a terminé le traitement. Ainsi, le canal peut être n'importe quoi, par exemple:
func warrior(name string, done chan bool) {
select {
case opponent := <-battle:
fmt.Printf("%s beat %s\n", name, opponent)
case battle <- name:
// I lost :-(
}
done <- true
}
func main() {
done := make(chan bool)
langs := []string{"Go", "C", "C++", "Java", "Perl", "Python"}
for _, l := range langs { go warrior(l, done) }
for _ = range langs { <-done }
}
Nous déclarons done := make(chan bool)
comme un canal qui reçoit la valeur bool, et envoie true
à la fin de warrior
à la place. Cela fonctionne! Vous pouvez également définir l' done
canaliser vers n'importe quel autre type, ça n'a pas d'importance.
1. Quel est donc avec l'étrange done <- struct{}{}
?
Il est juste un autre type, qui sera transféré à canal. C'est un vide struct, si vous êtes familier avec le code suivant:
type User struct {
Name string
Email string
}
struct{}
ne fait aucune différence, sauf qu'elle ne contient pas de champs, et struct{}{}
est juste un exemple. La meilleure fonctionnalité est qu'il ne coûte pas d'espace mémoire!
2. pour la boucle d'utilisation
nous créons 6 goroutines à exécuter en arrière-plan avec cette ligne:
for _, l := range langs { go warrior(l, done) }
Nous for _ = range langs { <-done }
, parce que la goroutine principale (où s'exécute la fonction principale) n'attend pas la fin des goroutins.
si nous n'incluons pas le dernier pour la ligne, il y a des chances que nous ne voyons pas de sorties(parce que les goroutines principales quittent avant que n'importe quel enfant goroutines exécute fmt.Printf
code, et quand main goroutine quittera, tous les enfants goroutines abandonneront avec, et n'auront aucune chance de courir de toute façon).
nous attendons donc que tous les goroutines finissent(il court jusqu'à la fin, et envoie un message à l' done
canal), puis quitte. done
canal voici un canal Bloqué, ce qui signifie <-done
bloc jusqu'à ce qu'un message est reçu du canal.
Nous avons 6 goroutines en arrière-plan, et de les utiliser pour la boucle, nous attendons jusqu'à ce que tous les goroutines envoyer un message qui signifie qu'il fini de s'exécuter(parce que done <-struct{}{}
est à la fin de la fonction).