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!
done
channel is used to receive notifications fromwarrior
method that indicates the worker is done processing. So the channel can be anything, for example:We declare
done := make(chan bool)
as a channel that receives bool value, and sendtrue
at the end ofwarrior
instead. This works! You can also define thedone
channel to any other type, it won't matter.1. So what is with the weird
done <- struct{}{}
?It is just another type that will be passed to channel. This is an empty struct, if you are familiar with the following:
struct{}
makes no difference except it contains no fields, andstruct{}{}
is just an instance out of it. The best feature is it does not cost memory space!2. for loop usage
We create 6 goroutines to run in the background with this line:
We use the
for _ = range langs { <-done }
, because the main goroutine(where main function runs) does not wait for goroutins to finish.If we does not include the last for line, chances are we see no outputs(because main goroutines quits before any child goroutines executes
fmt.Printf
code, and when main goroutine quits, all child goroutines will quit with it, and will not have any chance to run anyway).So we wait for all goroutines to finish(it runs to the end, and send a message to the
done
channel), then exits.done
channel here is a blocked channel, which means<-done
will block here until a message is received from the channel.We have 6 goroutines in the background, and use for loop, we wait until all goroutines send a message which means it finished running(because the
done <-struct{}{}
is at the the end of function).struct{}
is a type (in particular, a structure with no members). If you have a typeFoo
, you can create a value of that type in an expression withFoo{field values, ...}
. Putting this together,struct{}{}
is a value of the typestruct{}
, which is what the channel expects.The
main
function spawnswarrior
goroutines, which will write to thedone
channel when they have finished. The lastfor
block reads from this channel, ensuring thatmain
won't return until all the goroutines have finished. This is important because the program will exit whenmain
completes, irrespective of whether there are other goroutines running.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:
You could create it like so:
b := Book{}
, you only need one set of brackets because the Book struct has already been declared.struct{}{}
is a composite literal of typestruct{}
, 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 thelangs
have sentdone
messages.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:
See for instance the global var
errServerKeyExchange
in the file where the empty structrsaKeyAgreement
is defined.