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() errorThe 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())
}
}
}