// 用于访问上游传递给下游的值,通过一个key返回一个value或nil // 仅将其用于传输进程和API边界的请求范围,而不是其他的可选参数。我的理解是别用它来获取和进程控制无关的数据 // 数据存储的结构体是map[interface{}]interface{},而且是存储的key都是全局的 // 下面是一个使用demo // // // Package user defines a User type that's stored in Contexts. // package user // // import "context" // // // User is the type of value stored in the Contexts. // type User struct {...} // // // key is an unexported type for keys defined in this package. // // This prevents collisions with keys defined in other packages. // type key int // // // userKey is the key for user.User values in Contexts. It is // // unexported; clients use user.NewContext and user.FromContext // // instead of using this key directly. // var userKey key // // // NewContext returns a new Context that carries value u. // func NewContext(ctx context.Context, u *User) context.Context { // return context.WithValue(ctx, userKey, u) // } // // // FromContext returns the User value stored in ctx, if any. // func FromContext(ctx context.Context) (*User, bool) { // u, ok := ctx.Value(userKey).(*User) // return u, ok // } Value(key interface{}) interface{} }
此外还有一个值得一提的扩展接口canceler,实现这个接口的例子就有*cancelCtx and *timerCtx。
// A cancelCtx can be canceled. When canceled, it also cancels any children that implement canceler. type cancelCtx struct { Context
mu sync.Mutex // protects following fields done chanstruct{} // created lazily, closed by first cancel call children map[canceler]struct{} // set to nil by the first cancel call,实现了一个进程树,用map来当set用。 err error // set to non-nil by the first cancel call }
func(c *cancelCtx)Value(key interface{})interface{} { if key == &cancelCtxKey { // &cancelCtxKey is the key that a cancelCtx returns itself for. return c } return c.Context.Value(key) }
func(c *cancelCtx)Done() <-chanstruct{} { c.mu.Lock() // 先锁,就不会有重复创建的问题 if c.done == nil { // 惰性创建 c.done = make(chanstruct{}) } d := c.done // 此处的确是复制,但是chan本身就是一个指针类型 c.mu.Unlock() return d }
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to // implement Done and Err. It implements cancel by stopping its timer then // delegating to cancelCtx.cancel. type timerCtx struct { cancelCtx timer *time.Timer // Under cancelCtx.mu.
deadline time.Time }
func(c *timerCtx)Deadline()(deadline time.Time, ok bool) { return c.deadline, true }
// A valueCtx carries a key-value pair. It implements Value for that key and // delegates all other calls to the embedded Context. type valueCtx struct { Context key, val interface{} }
// stringify tries a bit to stringify v, without using fmt, since we don't // want context depending on the unicode tables. This is only used by // *valueCtx.String(). funcstringify(v interface{})string { switch s := v.(type) { case stringer: return s.String() casestring: return s } return"<not Stringer>" }
// 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. type emptyCtx int
func(*emptyCtx)Deadline()(deadline time.Time, ok bool) { return }
var ( background = new(emptyCtx) todo = new(emptyCtx) )
// Background returns a non-nil, empty Context. It is never canceled, has no // values, and has no deadline. It is typically used by the main function, // initialization, and tests, and as the top-level Context for incoming // requests. funcBackground()Context { return background }
// TODO returns a non-nil, empty Context. Code should use context.TODO when // it's unclear which Context to use or it is not yet available (because the // surrounding function has not yet been extended to accept a Context // parameter). funcTODO()Context { return todo }
// 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. funcWithCancel(parent Context)(ctx Context, cancel CancelFunc) { if parent == nil { panic("cannot create context from nil parent") } c := newCancelCtx(parent) propagateCancel(parent, &c) return &c, func() { c.cancel(true, Canceled) } }
// propagateCancel arranges for child to be canceled when parent is. funcpropagateCancel(parent Context, child canceler) { done := parent.Done() if done == nil { return// parent is never canceled,说明当前正在创建的child是取消构造树的根 } // 此处说明parent已经是一个可以取消的节点 select { case <-done: // parent is already canceled child.cancel(false, parent.Err()) return default: }
// 回溯判断自己的祖先是否是一个cancelCtx类型实例 // 若是:将child注册到parent中 // 若不是:则只需要监听parent和自己的取消信号即可 if p, ok := parentCancelCtx(parent); ok { p.mu.Lock() if p.err != nil { // 检查当前parent的parent是否被取消 // parent has already been canceled child.cancel(false, p.err) } else { // 在当前的parent依旧存活的情况下,为parent注册child if p.children == nil { p.children = make(map[canceler]struct{}) } p.children[child] = struct{}{} } p.mu.Unlock() } else { atomic.AddInt32(&goroutines, +1) // goroutines counts the number of goroutines ever created; for testing. gofunc() { select { case <-parent.Done(): child.cancel(false, parent.Err()) case <-child.Done(): } }() } }
// parentCancelCtx returns the underlying *cancelCtx for parent. // It does this by looking up parent.Value(&cancelCtxKey) to find // the innermost enclosing *cancelCtx and then checking whether // parent.Done() matches that *cancelCtx. (If not, the *cancelCtx // has been wrapped in a custom implementation providing a // different done channel, in which case we should not bypass it.) funcparentCancelCtx(parent Context)(*cancelCtx, bool) { done := parent.Done() // closedchan默认的一个chan if done == closedchan || done == nil { // cancelCtx的done返回的chan一定不是默认的或者nil returnnil, false } p, ok := parent.Value(&cancelCtxKey).(*cancelCtx) // cancelCtx实例的cancelCtxKey代表其本身 if !ok { returnnil, false } p.mu.Lock() ok = p.done == done // TODO 不太理解为啥需要这里的判断 p.mu.Unlock() if !ok { returnnil, false } return p, true }
//define a new type include a Context Field type otherContext struct { context.Context }
funcmain() {
//Construct a *cancelCtx type object ctxa, cancel := context.WithCancel(context.Background()) go work(ctxa, "work1")
//Construct a *timerCtx type object wrapped by *cancelCtx tm := time.Now().Add(3 * time.Second) ctxb, _ := context.WithDeadline(ctxa, tm) go work(ctxb, "work2")
oc := otherContext{ctxb} //Construct a *cancelCtx type object wrapped by oc ctxc := context.WithValue(oc, "key", "god andes,pass from main ") go workWithValue(ctxc, "work3")
//do something funcwork(ctx context.Context, name string) { for { select { case <-ctx.Done(): fmt.Printf("%s get msg to cancel\n", name) return default: fmt.Printf("%s is running \n", name) time.Sleep(1 * time.Second) } } }
//do something and pass values by context funcworkWithValue(ctx context.Context, name string) { for { select { case <-ctx.Done(): fmt.Printf("%s get msg to cancel\n", name) return default: value := ctx.Value("key").(string) fmt.Printf("%s is running value=%s \n", name, value) time.Sleep(1 * time.Second) } } }
work2 is running work3 is running value=god andes,pass from main work1 is running work2 is running work1 is running work3 is running value=god andes,pass from main work3 is running value=god andes,pass from main work1 is running work2 is running // work2, 3超时退出 work2 get msg to cancel work3 get msg to cancel work1 is running work1 is running work1 is running work1 is running work1 is running work1 is running work1 is running // work1 被取消退出 work1 get msg to cancel main stop