本身内容不是特别复杂,很容易看懂,例子也都很简单

context.go

首先给出了 Context 接口,有四个方法,从这里也可以看出 context 这个平时称作上下文的东西是干嘛用的。注释太多了,本来想全删掉,但下不了手,有一说一这注释实在太棒了,甚至想翻译一遍。

  1. // src/context/context.go ---- line 58
  2. // A Context carries a deadline, a cancellation signal, and other values across
  3. // API boundaries.
  4. //
  5. // Context's methods may be called by multiple goroutines simultaneously.
  6. type Context interface {
  7. // Deadline returns the time when work done on behalf of this context
  8. // should be canceled. Deadline returns ok==false when no deadline is
  9. // set. Successive calls to Deadline return the same results.
  10. Deadline() (deadline time.Time, ok bool)
  11. // Done returns a channel that's closed when work done on behalf of this
  12. // context should be canceled. Done may return nil if this context can
  13. // never be canceled. Successive calls to Done return the same value.
  14. // The close of the Done channel may happen asynchronously,
  15. // after the cancel function returns.
  16. //
  17. // WithCancel arranges for Done to be closed when cancel is called;
  18. // WithDeadline arranges for Done to be closed when the deadline
  19. // expires; WithTimeout arranges for Done to be closed when the timeout
  20. // elapses.
  21. //
  22. // Done is provided for use in select statements:
  23. //
  24. // // Stream generates values with DoSomething and sends them to out
  25. // // until DoSomething returns an error or ctx.Done is closed.
  26. // func Stream(ctx context.Context, out chan<- Value) error {
  27. // for {
  28. // v, err := DoSomething(ctx)
  29. // if err != nil {
  30. // return err
  31. // }
  32. // select {
  33. // case <-ctx.Done():
  34. // return ctx.Err()
  35. // case out <- v:
  36. // }
  37. // }
  38. // }
  39. //
  40. // See https://blog.golang.org/pipelines for more examples of how to use
  41. // a Done channel for cancellation.
  42. Done() <-chan struct{}
  43. // If Done is not yet closed, Err returns nil.
  44. // If Done is closed, Err returns a non-nil error explaining why:
  45. // Canceled if the context was canceled
  46. // or DeadlineExceeded if the context's deadline passed.
  47. // After Err returns a non-nil error, successive calls to Err return the same error.
  48. Err() error
  49. // Value returns the value associated with this context for key, or nil
  50. // if no value is associated with key. Successive calls to Value with
  51. // the same key returns the same result.
  52. //
  53. // Use context values only for request-scoped data that transits
  54. // processes and API boundaries, not for passing optional parameters to
  55. // functions.
  56. //
  57. // A key identifies a specific value in a Context. Functions that wish
  58. // to store values in Context typically allocate a key in a global
  59. // variable then use that key as the argument to context.WithValue and
  60. // Context.Value. A key can be any type that supports equality;
  61. // packages should define keys as an unexported type to avoid
  62. // collisions.
  63. //
  64. // Packages that define a Context key should provide type-safe accessors
  65. // for the values stored using that key:
  66. //
  67. // // Package user defines a User type that's stored in Contexts.
  68. // package user
  69. //
  70. // import "context"
  71. //
  72. // // User is the type of value stored in the Contexts.
  73. // type User struct {...}
  74. //
  75. // // key is an unexported type for keys defined in this package.
  76. // // This prevents collisions with keys defined in other packages.
  77. // type key int
  78. //
  79. // // userKey is the key for user.User values in Contexts. It is
  80. // // unexported; clients use user.NewContext and user.FromContext
  81. // // instead of using this key directly.
  82. // var userKey key
  83. //
  84. // // NewContext returns a new Context that carries value u.
  85. // func NewContext(ctx context.Context, u *User) context.Context {
  86. // return context.WithValue(ctx, userKey, u)
  87. // }
  88. //
  89. // // FromContext returns the User value stored in ctx, if any.
  90. // func FromContext(ctx context.Context) (*User, bool) {
  91. // u, ok := ctx.Value(userKey).(*User)
  92. // return u, ok
  93. // }
  94. Value(key interface{}) interface{}
  95. }

当然这里只是给了接口的定义,没有实际落地的结构体,于是标准库给出了几个具体实现,分别是 emptyCtx, cancelCtx, timerCtx, valueCtx 共四种,其中 timerCtx 是继承自 cancelCtx 的。当然作用也都很明了,直接看名称和注释就知道。

  1. // src/context/context.go ---- line 169
  2. // An emptyCtx is never canceled, has no values, and has no deadline. It is not
  3. // struct{}, since vars of this type must have distinct addresses.
  4. type emptyCtx int
  5. // src/context/context.go ---- line 339
  6. // A cancelCtx can be canceled. When canceled, it also cancels any children
  7. // that implement canceler.
  8. type cancelCtx struct {
  9. Context
  10. mu sync.Mutex // protects following fields
  11. done chan struct{} // created lazily, closed by first cancel call
  12. children map[canceler]struct{} // set to nil by the first cancel call
  13. err error // set to non-nil by the first cancel call
  14. }
  15. // src/context/context.go ---- line 452
  16. // A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
  17. // implement Done and Err. It implements cancel by stopping its timer then
  18. // delegating to cancelCtx.cancel.
  19. type timerCtx struct {
  20. cancelCtx
  21. timer *time.Timer // Under cancelCtx.mu.
  22. deadline time.Time
  23. }
  24. // src/context/context.go ---- line 523
  25. // A valueCtx carries a key-value pair. It implements Value for that key and
  26. // delegates all other calls to the embedded Context.
  27. type valueCtx struct {
  28. Context
  29. key, val interface{}
  30. }

可以看出 context 都是一层包一层的,像洋葱一样。每个结构体内都有一个 Context 接口的 parent 。所以如果结构体自己不重载(不该用这个词,但就这个意思嘛)接口中那四个方法的话,调用的就是 parent 的方法。


只有 emptyCtx 自己有那四个方法,实现了 Context 接口,所以我们经常都用 context.Background() 来作为最内部的 Context 然后再在外面一层一层地套(context.Background() 返回的就是一个 *emptyCtx,当然也可以自定义一个类型来实现 Context 接口用作最内层)。套一层 cancelCtx 那就可以 cancel 了,套一层 timerCtx 就可以有时间限制,时间到自动 cancel(注意,因为 timerCtx 是继承的 cancelCtx 噢,所以其实直接只要 timerCtx 也可以),再套 valueCtx 就可以存值了,要注意的是 valueCtx 存值是通过结构体字段,所以每对 k, v 都需要包一层 valueCtx 来存储。


以前一直以为 context 存值是通过哈希表,看了源码才知道是一层一对键值对,找不到就往上抛,让上层找。但这样的效率应该和顺序遍历一样吧,甚至不如,因为还有函数栈开销,但这种设计真的太棒了。


当然上一段是我第一时间的反应,没多加思考,仔细想想其实真正该用 context 存的值并不多,所以现在这样应该是最好的解决方案。简单高效且优雅。


关于 context 用处,在写 Golang 程序的时候,很容易就会起很多 goroutine,而开发者不是真的有精力去关心每个 goroutine 的状态,如果不注意很容易造成 goroutine 泄露,就像内存泄漏一样,非常难受,消耗资源不做事。而 context 就是用来优雅管理 goroutine 的。其实 Context 接口的注释真的说得很清楚了,还是找个时间翻译一下吧

example_test.go

这里有一些简单的 context 用例。都是很简单的。