可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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 %s\n", 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 } }
[1st Question]
done <- struct{}{}
How and Why do we need this weird-looking struct? Is it empty struct or anonymous struct? I googled it but couldn't find the right answer or documentation to explain about this.
The original source is from Andrew Gerrand's talk http://nf.wh3rd.net/10things/#10
Here
make(chan struct{})
done is a channel of type struct{}
So I tried with
done <- struct{}
But it is not working. Why do I need an extra brackets for this line?
done <- struct{}{}
[2nd Question]
for _ = range langs { <-done }
Why do I need this line? I know that this line is necessary because without this line, no output. But Why and what does this line do? And what makes it necessary in this code? I know that <-done is to receive values from the channel done and discard the received values. But why do I need to do this?
Thanks!
回答1:
Composite literals
Composite literals construct values for structs, arrays, slices, and maps and create a new value each time they are evaluated. They consist of the type of the value followed by a brace-bound list of composite elements. An element may be a single expression or a key-value pair.
struct{}{} is a composite literal of type struct{}, the type of the value followed by a brace-bound list of composite elements.
for _ = range langs { <-done } is waiting until all the goroutines for all the langs have sent done messages.
回答2:
Note that one interesting aspect of using struct{} for the type pushed to a channel (as opposed to int or bool), is that the size of an empty struct is... 0!
See the recent article "The empty struct" (March 2014) by Dave Cheney.
You can create as many struct{} as you want (struct{}{}) to push them to your channel: your memory won't be affected.
But you can use it for signaling between go routines, as illustrated in "Curious Channels".
And you retain all the other advantages linked to a struct:
- you can define methods on it (that type can be a method receiver)
- you can implement an interface (with said methods you just define on your empty struct)
- as a singleton
in Go you can use an empty struct, and store all your data in global variables. There will only be one instance of the type, since all empty structs are interchangeable.
See for instance the global var errServerKeyExchange in the file where the empty struct rsaKeyAgreement is defined.
回答3:
struct{} is a type (in particular, a structure with no members). If you have a type Foo, you can create a value of that type in an expression with Foo{field values, ...}. Putting this together, struct{}{} is a value of the type struct{}, which is what the channel expects.
The main function spawns warrior goroutines, which will write to the done channel when they have finished. The last for block reads from this channel, ensuring that main won't return until all the goroutines have finished. This is important because the program will exit when main completes, irrespective of whether there are other goroutines running.
回答4:
Good questions,
The whole point of the struct channel in this scenario is simply to signal the completion that something useful has happened. The channel type doesn't really matter, he could have used an int or a bool to accomplish the same effect. What's important is that his code is executing in a synchronized fashion where he's doing the necessary bookkeeping to signal and move on at key points.
I agree the syntax of struct{}{} looks odd at first because in this example he is declaring a struct and creating it in-line hence the second set of brackets.
If you had a pre-existing object like:
type Book struct{ }
You could create it like so: b := Book{}, you only need one set of brackets because the Book struct has already been declared.
回答5:
done channel is used to receive notifications from warrior method that indicates the worker is done processing. So the channel can be anything, for example:
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 {