context 使用背景
一个request可能对应启动多个goroutine去执行任务,我们可以通过context,实现对这些goroutine的生命周期的控制。同时,对于这些goroutine通用的常量变量,context可以提供存储。
使用demo
不断产生数字,直到程序退出
package main
import (
"fmt"
"context"
)
func main() {
gen := func(ctx context.Context) <-chan int {
dst := make(chan int)
n := 1
go func() {
for {
select {
case <-ctx.Done():
fmt.Println("Done")
return
// dst 是否可补充
case dst <- n:
fmt.Println("dst <- n",n)
n++
}
}
}()
return dst
}
ctx,cancel := context.WithCancel(context.Background())
defer cancel()
for n := range gen(ctx) {
fmt.Println(n)
if n == 5 {
break
}
}
}
源码分析:
interface 定义:
type Context interface {
Deadline() (deadline time.Time,ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
background 定义:
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
func Background() Context {
return background
}
func TODO() Context {
return todo
}
emptyCtx 定义:
// An emptyCtx is never canceled,has no values,and has no deadline. It is not
// struct{},since vars of this type must have distinct addresses.
// emptyCtx 不是ctx,而是int
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time,ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
WithCancel 定义:
func WithCancel(parent Context) (ctx Context,cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent,&c)
return &c,func() { c.cancel(true,Canceled) }
}
// 新cancelCtx类型
func newCancelCtx(parent Context) cancelCtx {
return cancelCtx{
Context: parent,done: make(chan struct{}),}
}
type cancelCtx struct {
Context
done chan struct{} // closed by the first cancel call.
mu sync.Mutex // 线程安全
children map[canceler]struct{} // set to nil by the first cancel call
err error // set to non-nil by the first cancel call
}
func (c *cancelCtx) Done() <-chan struct{} {
return c.done
}
func (c *cancelCtx) Err() error {
c.mu.Lock()
defer c.mu.Unlock()
return c.err
}
// cancel closes c.done,cancels each of c's children,and,if
// removeFromParent is true,removes c from its parent's children.
func (c *cancelCtx) cancel(removeFromParent bool,err error) {
if err == nil {
panic("context: internal error: missing cancel error")
}
c.mu.Lock()
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
c.err = err
close(c.done)
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false,err)
}
c.children = nil
c.mu.Unlock()
if removeFromParent {
removeChild(c.Context,c)
}
}
关联context:
// propagateCancel arranges for child to be canceled when parent is.
func propagateCancel(parent Context,child canceler) {
if parent.Done() == nil {
// 如果父ctx已经done,直接不关联
return // parent is never canceled
}
// 强转为cancelctx,如果都为cancelctx
if p,ok := parentCancelCtx(parent); ok {
p.mu.Lock()
if p.err != nil {
// parent has already been canceled
child.cancel(false,p.err)
} else {
// 加入到children节点中
if p.children == nil {
p.children = make(map[canceler]struct{})
}
p.children[child] = struct{}{}
}
p.mu.Unlock()
} else {
go func() {
// 如果parent完成,child也完成;如果child完成,不做处理
select {
case <-parent.Done():
child.cancel(false,parent.Err())
case <-child.Done():
}
}()
}
}
func parentCancelCtx(parent Context) (*cancelCtx,bool) {
for {
switch c := parent.(type) {
case *cancelCtx:
return c,true
case *timerCtx:
return &c.cancelCtx,true
case *valueCtx:
parent = c.Context
default:
return nil,false
}
}
}
原文链接:https://www.f2er.com/go/187192.html