effect
Pass notifications or data up the goroutine call tree. context.Context can be passed between multiple goroutine s. Compared with channel, it specifies the interface, which is more convenient to use and more standardized.
-
Exit notification: Notification can be passed to each goroutine on the goroutine call tree, making it easier to cancel actions on the multi-level goroutine call chain, and can easily cancel any goroutine on the call chain.
💡 It should be noted that we still need to manually handle the return value of Done() to decide whether to exit the goroutine. The context is only responsible for passing information, but the behavior of stopping the goroutine still needs to be handled by ourselves.
-
Pass data: data can be passed to each goroutine on the goroutine call tree
context.Context
understand
- context.Context is an interface type, which is a unified format type for users to use context in the go standard library;
- When you need to use Context, use the context.Context interface type, and generally pass context.Context in the first parameter of the function
4 interface methods
All the methods of the context.Context interface reflect its role: transfer notification and transfer data. The four interface methods are defined as follows:
// Deliver opt-out notice // When the Context is canceled, the Done() function returns a closed channel, so that multiple goroutine s can be notified. Whether it is actively calling CancelFunc or the timer set by Context expires, it will be notified to exit. func Done() ←chan struct{} // pass data // Pass data in the form of key-value, a Context can only pass a pair of key-value func Value(any) any // Get the reason for Context exit func Err() error // Get whether the deadline is set in the Context, if it is set, bool returns true, and returns the time of the deadline; otherwise returns bool as false func Deadline() (time.Time, bool)
4 specific Context types and API s
- emptyCtx
func Background() Context func TODO() Context
- cancelCtx
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
- timerCtx
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
- valueCtx
func WithValue(parent Context, key, val any) Context
Context hierarchy
💡 A Context can be used as the parent Context of other Contexts, it only needs to be specified when using the API to create a Context.
example
// parent context // ctx_parent -> ctx_level1 -> ctx_level2 ctx_parent, cancel := context.WithCancel(context.Background()) // level1 context // Expires in 6s ctx_level1, _ := context.WithTimeout(ctx_parent, time.Second * 6) // level2 context // If level1 expires, level2 will also expire ctx_level2, _ := context.WithCancel(ctx_level1) var wg sync.WaitGroup f := func(ctx context.Context, level int, interval int) { defer wg.Done() for { select { case <-ctx.Done(): fmt.Printf("level-%d received done\n", level) return default: fmt.Printf("level-%d running\n", level) time.Sleep(time.Duration(interval) * time.Second) } } } wg.Add(2) go f(ctx_parent, 0, 1) go f(ctx_level1, 1, 2) go f(ctx_level2, 2, 1) wg.Wait() wg.Add(1) cancel() wg.Wait()
Way of working
-
Notify to quit
The exit notification of the parent Context will be passed down to the child Context
However, the exit notification of the child Context will not be passed up to the parent Context
-
pass by value
The child Context can find the key-value in the parent Context
But the parent Context cannot find the key-value in the child Context, the example is as follows
root := context.Background() ctx_parent := context.WithValue(root, keyType("parent"), "parent-value") ctx_child := context.WithValue(ctx_parent, keyType("child"), "child-value") var wg sync.WaitGroup type keyType string // WithValue needs to use a custom key type to prevent conflicts with libraries using context f := func(ctx context.Context, name string, keyname keyType) { defer wg.Done() fmt.Printf("[goroutine %s] ctx[%v]=%v\n", name, keyname, ctx.Value(keyname)) } wg.Add(2) go f(ctx_parent, "goroutine-1", keyType("child")) // Can't get the value whose key is child go f(ctx_child, "goroutine-2", keyType("parent")) // You can get the value whose key is parent wg.Wait()
Additional Details and Considerations
Go standard library contex t documentation
- Don't use context.Context as a member of struct, but should be passed by function parameter passing, usually Context is placed as the first parameter, and the name is usually named ctx
- Do not pass a Context with a value of nil, use context.TODO() instead
- The Value of context should not be used to pass some business-related parameters (not for passing optional parameters to functions.)
- Context is concurrently safe (Contexts are safe for simultaneous use by multiple goroutines)
- WithDeadline(parent context.Context, d time.Time), if the parent also has a deadline, and the parent's deadline is earlier than d, then the deadline time of the Context returned by WithDeadline is subject to the parent
- CancelFunc type, this function type is the second return value of the With series of functions (except WithValue), you can directly call this function to broadcast the exit notification.
It is concurrently safe to call CancelFunc in different goroutine s at the same time. If they are called at the same time, subsequent calls after the first call will not be affected. (A CancelFunc may be called by multiple goroutines simultaneously. After the first call, subsequent calls to a CancelFunc do nothing.)