[笔记] Go Context
Last updated: 2020/10/20 Published at: 2020/10/20
Overview
In Go servers, each incoming request is handled in its own goroutine. Request handlers often start additional goroutines to access backends such as databases and RPC services. The set of goroutines working on a request typically needs access to request-specific values such as the identity of the end user, authorization tokens, and the request’s deadline. When a request is canceled or times out, all the goroutines working on that request should exit quickly so the system can reclaim any resources they are using.
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context. The chain of function calls between them must propagate the Context, optionally replacing it with a derived Context created using WithCancel, WithDeadline, WithTimeout, or WithValue. When a Context is canceled, all Contexts derived from it are also canceled.
The WithCancel, WithDeadline, and WithTimeout functions take a Context (the parent) and return a derived Context (the child) and a CancelFunc. Calling the CancelFunc cancels the child and its children, removes the parent’s reference to the child, and stops any associated timers. Failing to call the CancelFunc leaks the child and its children until the parent is canceled or the timer fires. The go vet tool checks that CancelFuncs are used on all control-flow paths.
Rules
Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx:
1func DoSomething(ctx context.Context, arg Arg) error { 2 // ... use ctx ... 3}
Do not pass a nil Context, even if a function permits it. Pass context.TODO if you are unsure about which Context to use.
Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.
The same Context may be passed to functions running in different goroutines; Contexts are safe for simultaneous use by multiple goroutines.
Usages
WithCancel
Introduce
WithCancel returns a copy of parent with a new Done channel. The returned context’s Done channel is closed when the returned cancel function is called or when the parent context’s Done channel is closed, whichever happens first.
Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete.
Example
1package main
2
3import (
4 "context"
5 "fmt"
6 "time"
7)
8
9func main() {
10 gen := func(ctx context.Context) <- chan int{
11 dst := make(chan int)
12 n := 1
13 go func() {
14 for{
15 select {
16 case <-ctx.Done():
17 fmt.Println("gen exit")
18 return
19 case dst<-n:
20 n++
21 }
22 }
23
24 }()
25 return dst
26 }
27
28 ctx,cancel := context.WithCancel(context.Background())
29
30 for n := range gen(ctx){
31 fmt.Println(n)
32 if n==5 {
33 break
34 }
35 }
36 cancel()
37
38 time.Sleep(500*time.Millisecond)
39}
WithDeadLine
Introduce
WithDeadline returns a copy of the parent context with the deadline adjusted to be no later than d. If the parent’s deadline is already earlier than d, WithDeadline(parent, d) is semantically equivalent to parent. The returned context’s Done channel is closed when the deadline expires, when the returned cancel function is called, or when the parent context’s Done channel is closed, whichever happens first.
Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete.
Example
1package main
2
3import (
4 "context"
5 "fmt"
6 "time"
7)
8
9const shortDuration = 1 * time.Millisecond
10
11func main() {
12 d := time.Now().Add(shortDuration)
13 ctx, cancel := context.WithDeadline(context.Background(), d)
14
15 defer cancel()
16
17 select {
18 case <-time.After(1 * time.Second):
19 fmt.Println("overslept")
20 case <-ctx.Done():
21 fmt.Println(ctx.Err())
22 }
23}
WithTimeout
Introduce
WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete.
WithValue
Introduce
WithValue returns a copy of parent in which the value associated with key is val.
Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.
The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using context. Users of WithValue should define their own types for keys. To avoid allocating when assigning to an interface{}, context keys often have concrete type struct{}. Alternatively, exported context key variables’ static type should be a pointer or interface.
Example
1package main
2
3import (
4 "context"
5 "fmt"
6 "time"
7)
8
9type favContextKey string
10
11func main() {
12 k := favContextKey("language")
13 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
14 ctx2 := context.WithValue(ctx, k, "-> Route 2")
15
16 for num := range gen(ctx2){
17 fmt.Println(num)
18 if num==5{
19 break
20 }
21 }
22
23 cancel()
24 fmt.Println("Call Cancel!")
25 time.Sleep(1 * time.Second)
26}
27
28func gen(ctx context.Context) <-chan int {
29 dst := make(chan int)
30 n := 1
31 k := favContextKey("language")
32 go func() {
33 for {
34 select {
35 case <-ctx.Done():
36 fmt.Println("-> Route 1")
37 return
38 case dst <- n:
39 fmt.Println(ctx.Value(k))
40 n++
41 }
42 }
43 }()
44 return dst
45}