To be candid I never really understood what select statements did. I understood that they handled values returned from
channels similar to a switch statement, minus the channels.
It wasn't until I went through the Select chapter of TDD with go, that I really understood what they did.
What helped me understand was the the problem being solved in the chapter so I'll use snippets of those as my examples.
The question was the race to url using http.GET and return the faster one. Let's look at the initial approach
Version 1:
func Racer(a, b string) (winner string) {
startA := time.Now()
http.Get(a)
aDuration := time.Since(startA)
startB := time.Now()
http.Get(b)
bDuration := time.Since(startB)
if aDuration < bDuration {
return a
}
return b
}This was the initial solution, this works but we can leverage concurrency in Go to make this more interesting and faster.
Version 2:
func Racer(a, b string) (winner string) {
select {
case <-ping(a):
return a
case <-ping(b):
return b
}
}
func ping(url string) chan struct{} {
ch := make(chan struct{})
go func() {
http.Get(url)
close(ch)
}()
return ch
}You can wait for values to be sent to a channel with myVar := <-ch. This is a blocking call, as you're waiting for a
value. This bit I was clear about.
What select lets you do is wait on multiple channels.
These two statements, made me go "ah ha!".
Understanding that select helps synchronise processes, for multiple channels was the missing piece of the puzzle for me.
- Always
makechannels, as go initilizes types to it's zero value when usingvar name type. The zero value for channels isniland you cannot send tonilchannles. So the process will forever block. - If you do not need to send values over channels use,
struct{}as it consumes the least amount of memory in terms of allocation.