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!

20
demandé sur peterSO 2013-12-27 05:37:23

5 réponses

Composite littéraux

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.

21
répondu peterSO 2013-12-27 01:53:05

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.

27
répondu VonC 2017-05-23 12:26:10
  1. struct{} est un type (en particulier, une structure sans membres). Si vous avez un type Foo, vous pouvez créer une valeur de ce type dans une expression avec Foo{field values, ...}. Mettre cela ensemble, struct{}{} est une valeur de type struct{}, ce que la chaîne attend.

  2. main fonction engendre warrior goroutines, qui écriront à la done canaliser quand ils ont terminé. Le dernier for bloc lit sur ce canal, veiller à ce que main ne reviendra pas tant que les goroutines n'auront pas fini. Ceci est important parce que le programme se terminera quand main complète, qu'il y ait ou non d'autres goroutines en cours d'exécution.

7
répondu James Henstridge 2013-12-27 01:47:33

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.

3
répondu Ralph Caraveo 2013-12-27 01:48:15

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).

1
répondu cizixs 2016-12-27 09:26:02