golang-context

剖析一下golang的context包

Context

Context 结构体

Context 承载着deadline,cancelation signal,和其他值。Context的方法都是协程安全的。
context结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
type Context interface {
4// Deadline 返回应该取消代表此上下文完成的工作的时间
4// 如果返回ok==false,表示deadline没有被设置
4// 连续调用Deadline,会返回相同的结果
4Deadline() (deadline time.Time, ok bool)

4// Done 返回一个channel,context退出将会关闭这个channel
4// Done 有可能返回nil,表示context没有被取消
4// 连续调用Deadline,会返回相同的结果
4//
4// WithCancel 当cancel被调用时,将会调用Done去关闭
4// WithDeadline 当deadline到期时调用Done
4// WithTimeout 当超时时,调用Done
4//
4// Done 为了在select中使用:
4// Stream使用DoSomething生成值并将它们发送到out,直到DoSomething返回错误或ctx.Done关闭。
4// func Stream(ctx context.Context, out chan<- Value) error {
4// for {
4// v, err := DoSomething(ctx)
4// if err != nil {
4// return err
4// }
4// select {
4// case <-ctx.Done():
4// return ctx.Err()
4// case out <- v:
4// }
4// }
4// }
4Done() <-chan struct{}

4// Err 如果Done没有被关闭,Err返回nil
4// 如果Done被关闭,Err返回非空error
4// 如果context被cancel,则Canceled;或者如果context deadline已过,则DeadlineExceeded
4// Err返回非零错误后,对Err的连续调用返回相同的错误。
4Err() error

4// Value 返回和context相关的key或nil
4// 如果没有相关连的key,使用相同的key连续调用Value会返回相同的结果
4// 仅将context值用于转换进程和API边界的请求范围数据,而不是将可选参数传递给函数。
4//
4// key定义context中的特定值,希望在Context中存储值的函数通常在全局中分配键变量然后使用该键作为context.WithValue和Context.Value的参数。
4// key可以是支持相等的任何类型;包应该将键定义为未导出类型以避免冲突。
4//
4// 定义Context键的包应该为使用该键存储的值提供类型安全的访问器:
4//
4// // Package user defines a User type that's stored in Contexts.
4// package user
4//
4// import "context"
4//
4// // User is the type of value stored in the Contexts.
4// type User struct {...}
4//
4// // key is an unexported type for keys defined in this package.
4// // This prevents collisions with keys defined in other packages.
4// type key int
4//
4// // userKey is the key for user.User values in Contexts. It is
4// // unexported; clients use user.NewContext and user.FromContext
4// // instead of using this key directly.
4// var userKey key
4//
4// // NewContext returns a new Context that carries value u.
4// func NewContext(ctx context.Context, u *User) context.Context {
4// return context.WithValue(ctx, userKey, u)
4// }
4//
4// // FromContext returns the User value stored in ctx, if any.
4// func FromContext(ctx context.Context) (*User, bool) {
4// u, ok := ctx.Value(userKey).(*User)
4// return u, ok
4// }
4Value(key interface{}) interface{}
}

context 函数

初始化

context有两个初始化函数,两个函数都是返回一个非nil的空context。

1
2
func Background() Context
func TODO() Context

  • Background():永远不会被取消,没有value,没有deadline。它通常由主函数,初始化和测试使用,并作为传入请求的顶级Context。
  • TODO():当不清楚使用哪个Context或者它还不可用时(因为周围的函数尚未扩展为接受Context参数)。静态分析工具可识别TODO,以确定上下文是否在程序中正确传播。

    添加参数

    1
    2
    3
    4
    5
    6
    type CancelFunc func()

    func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
    func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
    func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
    func WithValue(parent Context, key, val interface{}) Context
  • CancelFunc():告诉操作者怎样停止操作,并且不等待工作停止。第一次调用后,对CancelFunc的后续调用不执行任何操作。

  • WithCancel():返回带有新Done通道的副本。当返回的cancel函数被调用,或者parent的Done管道被关闭,都会关闭ctx的Done管道。
  • WithDeadline():返回带有新调整deadline(截止日期为d)的parent副本。如果d在parent的deadline之后,则副本的deadline等同于parent的deadline。当截止日期到期,或返回的CancelFunc被调用,或parent的Done管道关闭,都会导致副本的Done管道关闭。调用CancelFunc 会释放context与其关联的资源,因此代码应在此context运行的操作完成后立即调用cancel。
  • WithTimeout():返回WithDeadline(parent, time.Now().Add(timeout))
  • WithValue():返回带有key为val的parent副本。context的value仅用于转换进程和API的请求范围数据,而不是将可选参数传递给函数。参数key必须是可以比较的,不应该是字符串类型或任何其他内置类型,以避免使用上下文的包之间的冲突。WithValue的调用者应该为key定义他们自己的类型。为了避免在分配接口{}时分配,上下文键通常具有具体类型struct {},或者导出的context关键变量的静态类型应该是指针或接口。

内部类型

interface

context包里面除了Context接口,还有canceler接口。caceler用于With*函数的返回值cancel的值

1
2
3
4
type canceler interface {
4cancel(removeFromParent bool, err error)
4Done() <-chan struct{}
}

struct

context包里面emptyCtx,cancelCtx,timerCtx,valueCtx结构
emptyCtx是默认的context,是Background和TODO的直接返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
type emptyCtx int

func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
4return
}

func (*emptyCtx) Done() <-chan struct{} {
4return nil
}

func (*emptyCtx) Err() error {
4return nil
}

func (*emptyCtx) Value(key interface{}) interface{} {
4return nil
}

func (e *emptyCtx) String() string {
4switch e {
4case background:
44return "context.Background"
4case todo:
44return "context.TODO"
4}
4return "unknown empty Context"
}

var (
4background = new(emptyCtx)
4todo = new(emptyCtx)
)

func Background() Context {
4return background
}

func TODO() Context {
4return todo
}

cancelCtx结构用于在WithCancel()返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
type cancelCtx struct {
4Context

4mu sync.Mutex // protects following fields
4done chan struct{} // created lazily, closed by first cancel call
4children map[canceler]struct{} // set to nil by the first cancel call
4err error // set to non-nil by the first cancel call
}

func (c *cancelCtx) Done() <-chan struct{} {
4c.mu.Lock()
4if c.done == nil {
44c.done = make(chan struct{})
4}
4d := c.done
4c.mu.Unlock()
4return d
}

func (c *cancelCtx) Err() error {
4c.mu.Lock()
4err := c.err
4c.mu.Unlock()
4return err
}

func (c *cancelCtx) String() string {
4return fmt.Sprintf("%v.WithCancel", c.Context)
}

// 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) {
4if err == nil {
44panic("context: internal error: missing cancel error")
4}
4c.mu.Lock()
4if c.err != nil {
44c.mu.Unlock()
44return // already canceled
4}
4c.err = err
4if c.done == nil {
44c.done = closedchan
4} else {
44close(c.done)
4}
4for child := range c.children {
44// NOTE: acquiring the child's lock while holding parent's lock.
44child.cancel(false, err)
4}
4c.children = nil
4c.mu.Unlock()

4if removeFromParent {
44removeChild(c.Context, c)
4}
}

timerCtx结构用于WithDeadline()的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
type timerCtx struct {
4cancelCtx
4timer *time.Timer // Under cancelCtx.mu.

4deadline time.Time
}

func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
4return c.deadline, true
}

func (c *timerCtx) String() string {
4return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, time.Until(c.deadline))
}

func (c *timerCtx) cancel(removeFromParent bool, err error) {
4c.cancelCtx.cancel(false, err)
4if removeFromParent {
44// Remove this timerCtx from its parent cancelCtx's children.
44removeChild(c.cancelCtx.Context, c)
4}
4c.mu.Lock()
4if c.timer != nil {
44c.timer.Stop()
44c.timer = nil
4}
4c.mu.Unlock()
}

valueCtx结构用WithValue()函数的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type valueCtx struct {
4Context
4key, val interface{}
}

func (c *valueCtx) String() string {
4return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
}

func (c *valueCtx) Value(key interface{}) interface{} {
4if c.key == key {
44return c.val
4}
4return c.Context.Value(key)
}