Timeouts are important for programs that connect to external resources or that otherwise need to bound execution time. Implementing timeouts in Go is easy and elegant thanks to channels and select
.
[maxwell@oracle-db-19c Day04]$ vim timeouts.go
[maxwell@oracle-db-19c Day04]$ cat timeouts.go
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string, 1)
go func(){
time.Sleep(2 * time.Second)
c1 <- "result 1"
}()
select {
case res := <-c1:
fmt.Println(res)
case <- time.After(1 * time.Second):
fmt.Println("timeout 1")
}
c2 := make(chan string, 1)
go func() {
time.Sleep(2 * time.Second)
c2 <- "result 2"
}()
select {
case res := <-c2:
fmt.Println(res)
case <-time.After(3 * time.Second):
fmt.Println("timeout 2")
}
}
[maxwell@oracle-db-19c Day04]$ go run timeouts.go
timeout 1
result 2
[maxwell@oracle-db-19c Day04]$
Basic sends and receives on channels are blocking. However, we can use select
with a default
clause to implement non-blocking sends, receives, and even non-blocking multi-way select
s.
[maxwell@oracle-db-19c Day04]$ vim non_blocking_channel_operations.go
[maxwell@oracle-db-19c Day04]$ cat non_blocking_channel_operations.go
package main
import "fmt"
func main() {
messages := make(chan string)
signals := make(chan bool)
select {
case msg := <-messages:
fmt.Println("received messsage", msg)
default:
fmt.Println("no message received")
}
msg := "hi"
select {
case messages <- msg:
fmt.Println("sent message", msg)
default:
fmt.Println("no message sent")
}
select {
case msg := <-messages:
fmt.Println("received message", msg)
case sig := <-signals:
fmt.Println("received signal", sig)
default:
fmt.Println("no activity")
}
}
[maxwell@oracle-db-19c Day04]$ go run non_blocking_channel_operations.go
no message received
no message sent
no activity
[maxwell@oracle-db-19c Day04]$
Closing a channel indicates that no more values will be sent on it. This can be useful to communicate completion to the channel’s receivers.
[maxwell@oracle-db-19c Day04]$ vim closing_channels.go
[maxwell@oracle-db-19c Day04]$ cat closing_channels.go
package main
import "fmt"
func main() {
jobs := make(chan int, 5)
done := make(chan bool)
go func(){
for {
j, more := <-jobs
if more {
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
done <- true
return
}
}
}()
for j:=1;j <= 3; j++ {
jobs <- j
fmt.Println("sent job", j)
}
close(jobs)
fmt.Println("sent all jobs")
<-done
}
[maxwell@oracle-db-19c Day04]$ go run closing_channels.go
sent job 1
sent job 2
sent job 3
sent all jobs
received job 1
received job 2
received job 3
received all jobs
[maxwell@oracle-db-19c Day04]$
how for
and range
provide iteration over basic data structures. We can also use this syntax to iterate over values received from a channel.
[maxwell@oracle-db-19c Day04]$ vim range_over_channels.go
[maxwell@oracle-db-19c Day04]$ cat range_over_channels.go
package main
import "fmt"
func main(){
queue := make(chan string, 2)
queue <- "one"
queue <- "two"
close(queue)
for elem := range queue{
fmt.Println(elem)
}
}
[maxwell@oracle-db-19c Day04]$ go run range_over_channels.go
one
two
[maxwell@oracle-db-19c Day04]$
We often want to execute Go code at some point in the future, or repeatedly at some interval. Go’s built-in timer and ticker features make both of these tasks easy. We’ll look first at timers and then at tickers.
[maxwell@oracle-db-19c Day04]$ vim timers.go
[maxwell@oracle-db-19c Day04]$ cat timers.go
package main
import (
"fmt"
"time"
)
func main() {
timer1 := time.NewTimer(2 * time.Second)
<-timer1.C
fmt.Println("Timer 1 fired")
timer2 := time.NewTimer(time.Second)
go func(){
<-timer2.C
fmt.Println("Timer 2 fired")
}()
stop2 := timer2.Stop()
if stop2 {
fmt.Println("Timer 2 stopped")
}
time.Sleep(2 * time.Second)
}
[maxwell@oracle-db-19c Day04]$ go run timers.go
Timer 1 fired
Timer 2 stopped
[maxwell@oracle-db-19c Day04]$