import "context"
Go servers treat each incoming request in its own goroutine
Each request handler may spawn other goroutines to access hardware( IO operation)
When a request is cancelled or timeout, goroutines should exit quickly so the system can reclaim resources
type Context interface {
// Done returns a channel that is closed when this Context is canceled
// or times out.
Done() <-chan struct{}
// Err indicates why this context was canceled, after the Done channel
// is closed.
Err() error
// Deadline returns the time when this Context will be canceled, if any.
Deadline() (deadline time.Time, ok bool)
// Value returns the value associated with key or nil if none.
Value(key interface{}) interface{}
}
Done() <-chan struct{}
The Done method returns a channel that acts as a cancelation signal to functions running on behalf of the Context: when the channel is closed, the functions should abandon their work and return
Err() error
The Err method returns an error indicating why the Context was canceled.
context.TODO()
context.Background()
func main() {
ctx := context.TODO()
}
First, we create our context
func printNums() {
for {
time.Sleep(1 * time.Second)
println(rand.Int())
}
}
Next, we write a func that we will treat as a goroutine doing dumb work.
Like printing numbers
WithCancel(parent Context) (ctx Context, cancel CancelFunc)
Takes in parent context
Spits out new context, and a cancel function
package main
import (
"context"
"math/rand"
"time"
)
func main() {
todo := context.TODO()
ctx, cancelFunc := context.WithCancel(todo)
go printNums(ctx)
}
func printNums(ctx context.Context) {
for {
time.Sleep(1 * time.Second)
println(rand.Int())
}
}
package main
import (
"context"
"math/rand"
"time"
)
func main() {
todo := context.TODO()
ctx, cancelFunc := context.WithCancel(todo)
go printNums(ctx)
//wait and see numbers getting print
time.Sleep(5 * time.Second)
//cancel the context
cancelFunc()
//wait and see if its still printing numbers
time.Sleep(5 * time.Second)
}
func printNums(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
time.Sleep(1 * time.Second)
println(rand.Int())
}
}
}
context.WithTimeout(ctx context.Context, d time.Duration)
func main() {
todo := context.TODO()
ctx, cancelFunc := context.WithTimeout(todo, 5*time.Second)
defer cancelFunc()
go printNums(ctx)
//wait and see if its still printing numbers
time.Sleep(10 * time.Second)
}
func printNums(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
time.Sleep(1 * time.Second)
println(rand.Int())
}
}
}
context.WithTimeout(ctx context.Context, d time.Duration)
func main() {
todo := context.TODO()
d := time.Now().Add(5 * time.Second)
ctx, cancelFunc := context.WithDeadline(todo, d)
defer cancelFunc()
go printNums(ctx)
//wait and see if its still printing numbers
time.Sleep(10 * time.Second)
}
func printNums(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
time.Sleep(1 * time.Second)
println(rand.Int())
}
}
}
when a parent context is cancelled, all their child's contexts are cancelled as well.
func main() {
todo := context.TODO()
d := time.Now().Add(5 * time.Second)
ctx, cancelFunc := context.WithDeadline(todo, d)
defer cancelFunc()
go printNums(ctx)
<-ctx.Done()
println("5 seconds complete")
//wait and see if its still printing numbers
time.Sleep(5 * time.Second)
}
func printMoreNums(ctx context.Context) {
time.Sleep(10 * time.Millisecond)
println(rand.Int())
}
func printNums(ctx context.Context) {
go printMoreNums(ctx)
go printMoreNums(ctx)
for {
select {
case <-ctx.Done():
return
default:
time.Sleep(1 * time.Second)
println(rand.Int())
}
}
}