Syntax
The select statement waits for multiple send or receive operations simultaneously.
// Blocks until there's data available on ch1 or ch2
select {
case <-ch1:
fmt.Println("Received from ch1")
case <-ch2:
fmt.Println("Received from ch2")
}
- The statement blocks as a whole until one of the operations becomes unblocked.
- If several cases can proceed, a single one of them will be chosen at random.
Send and receive operations on a nil channel block forever. This can be used to disable a channel in a select statement:
ch1 = nil // Disables this channel
select {
case <-ch1:
fmt.Println("Received from ch1") // Will not happen
case <-ch2:
fmt.Println("Received from ch2")
}
Example - Wrong way of concurrency
Consider the methods:
func Every500ms(c1 chan string) {
c1 <- "Every 500ms"
time.Sleep(time.Millisecond * 500)
}
func Every2Seconds(c2 chan string) {
c2 <- "Every 2 Seconds"
time.Sleep(time.Second * 2)
}
Main:
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
for {
go Every500ms(c1)
}
}()
go func() {
for {
go Every2Seconds(c2)
}
}()
// This will print 1 after another.. NEVER EVER DO THIS!
for {
fmt.Println(<-c1)
fmt.Println(<-c2)
}
}
Output:
Every 500ms
Every 2 Seconds
Every 500ms
Every 2 Seconds
Every 500ms
Every 2 Seconds
...
Example - Correct way for concurrency using select
Main:
// Concurrency allows parallelism
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
for {
go Every500ms(c1)
}
}()
go func() {
for {
go Every2Seconds(c2)
}
}()
// Correct approach
for {
select {
case msg1 := <-c1:
fmt.Println(msg1)
case msg2 := <-c2:
fmt.Println(msg2)
}
}
}
Output:
Every 2 Seconds
Every 2 Seconds
Every 2 Seconds
Every 500ms
Every 2 Seconds
Every 500ms
Every 2 Seconds
Every 500ms
Every 500ms
Every 2 Seconds
Default case
The default case is always able to proceed and runs if all other cases are blocked.
// Never blocks
select {
case x := <-ch:
fmt.Println("Received", x, "from ch")
default:
fmt.Println("Nothing available on ch")
}
Example - Default case
Lets play with the time channels. time.Tick and time.After return channels.
func main() {
// tick and After return time channels
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}
Output:
.
.
.
tick.
.
.
tick.
.
tick.
.
.
tick.
.
.
BOOM!
Handling timeouts
The function time.After is part of the standard library; it waits for a specified time to elapse and then sends the current time on the returned channel.
Example - Handling timeout
func main() {
response := make(chan *http.Response, 1)
errors := make(chan *error)
go func() {
resp, err := http.Get("http://abc.alpha.beta.gama.com")
if err != nil {
errors <- &err
}
response <- resp
}()
for {
select {
case r := <-response:
fmt.Printf("%s", r.Body)
return
case err := <-errors:
log.Fatal(*err)
case <-time.After(200 * time.Millisecond):
fmt.Printf("Timed out!")
return
}
}
}
Block forever
// Block forever
select {}
A select statement blocks until at least one of it’s cases can proceed. With zero cases this will never happen.
http://www.golangbootcamp.com/book/concurrency