1.包注释翻译
//Incoming requests to a server should create a Context, and outgoing
//calls to servers should accept a Context. The chain of function
//calls between them must propagate the Context, optionally replacing
//it with a derived Context created using WithCancel, WithDeadline,
//WithTimeout, or WithValue. When a Context is canceled, all
//Contexts derived from it are also canceled.
/*
传入到服务器的请求应该创建一个context和传出调用服务器应该接受一个context。
函数链之间的调用必须传播contetx,可选择替换它使用 WithCancel,
WithDeadline、WithTimeout,或WithValue。
当一个context被取消时,所有的由它派生的context也会被取消。
*/
//The WithCancel, WithDeadline, and WithTimeout functions take a
//Context (the parent) and return a derived Context (the child) and a
//CancelFunc. Calling the CancelFunc cancels the child and its
//children, removes the parent's reference to the child, and stops
//any associated timers. Failing to call the CancelFunc leaks the
//child and its children until the parent is canceled or the timer
//fires. The go vet tool checks that CancelFuncs are used on all
//control-flow paths.
/*
WithCancel, WithDeadline, and WithTimeout 这三个函数携带一个 context 并返回
一个衍生的子context和一个取消函数(cancel)。调用这个 CancelFunc 会取消它的子
context以及子context衍生的所有context,移除父context对子context的引用,然后停止
所有相关的计数器。 未能调用 CancelFunc 会泄漏 child context 和它的孩子,直到父母
被取消或 timer fire.go vet 工具检查 CancelFuncs 是否用于所有控制流路径
*/
//Programs that use Contexts should follow these rules to keep
//interfaces consistent across packages and enable static
//analysis tools to check context propagation:
/*
使用context的程序应该遵循下列规则来确保接口跨包一致和开启静态分析工具检查context传播:
*/
// Do not store Contexts inside a struct type; instead, pass a Context
// explicitly to each function that needs it. The Context should be
// the first parameter, typically named ctx:
/*
不要在一个结构体里面存储context;相反的,在每一个需要context的函数间显视的传递
它。context应该作为第一个参数,且被命名为ctx,例如:
func DoSomething(ctx context.Context, arg Arg) error {
// ... use ctx ...
}
*/
// Do not pass a nil Context, even if a function permits it.
// Pass context.TODO if you are unsure about which Context to use.
/*
即使函数允许,你也不要传递一个空context。如果你不确定使用哪个context,那就
传递一个 context.TODO
*/
// Use context Values only for request-scoped data that transits
// processes and APIs, not for passing optional parameters to functions.
/*
仅将 context values 用于传输进程和API 的请求数据,而不将可选参数传递给函数
(就是说像用户信息啊啥的不要包在context里面传给下一个逻辑函数之类的)
*/
// The same Context may be passed to functions running in different
// goroutines; Contexts are safe for simultaneous use by
// multiple goroutines.
/*
一个相同的context可能会在多个不同的goroutine间传播;对于多个goroutine的同时使用,
context是安全的。
*/
// See https://blog.golang.org/context for
// example code for a server that uses Contexts.
2.函数源码解析
主结构Context:
对源码的注释和解读穿插在下列完整源代码中:
// A Context carries a deadline, a cancellation signal, and other values across API boundaries.
// 一个context携带一个 deadline,取消信号和其他值通过 api 边界
// Context's methods may be called by multiple goroutines simultaneously.
// context的方法可能被多个goroutines同时调用
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
/*
deadline 返回代表该 context 的工作应该被取消的时间。当deadline没有
被设置时,返回 ok == false。连续的调用Deadline返回相同的结果。
*/
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
// The close of the Done channel may happen asynchronously,
// after the cancel function returns.
/*
Done 返回一个当代表这个上下文完成的工作应该被取消时关闭的通道。 如果此上下文永远无法
取消,则 Done 可能返回 nil。 对 Done 的连续调用返回相同的值。 Done 通道的关闭可能
会异步发生,在取消函数返回后。
*/
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
/*
WithCancel 安排Done在调用cancel时关闭;
WithDeadline 安排Done在deadline过期时关闭;
WithTimeout 安排Done在超时时关闭。
*/
//
// Done is provided for use in select statements:
// Done 被用于 select 语句
//
// 下面是一个使用例子:
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See https://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancellation.
Done() <-chan struct{}
// If Done is not yet closed, Err returns nil.
// If Done is closed, Err returns a non-nil error explaining why:
// Canceled if the context was canceled
// or DeadlineExceeded if the context's deadline passed.
// After Err returns a non-nil error, successive calls to Err return the same error.
/*
如果Done还没被关闭,Err返回nil。如果Done被关闭了,Err返回一个非空的解释:
1.如果context被cancel则为Canceled。
2.或者context过了deadline,则为DeadlineExceeded。
在Err返回一个非空错误后,连续的调用会返回相同的error
*/
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
/* Value 返回与 key 的context关联的值,如果没有与 key 关联的值,则返回 nil。
使用相同的键连续调用 Value 会返回相同的结果。
*/
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
/*
使用 context values 仅当进程转移和api边界的跨数据请求,不做函数间的值传递
*/
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
/*
key 标志context中的特定值。希望在 Context 中存储值的函数通常在全局变量中分配
一个键,然后使用该键作为 context.WithValue 和 Context.Value 的参数。
key可以是任意支持相等的类型;包应将键定义为未导出的类型以避免冲突。 (小写)
*/
//
// Packages that define a Context key should provide type-safe accessors
// for the values stored using that key:
// 定义 Context key的包应该为使用该key存储的value提供类型安全的访问器
// 上述使用说明例子:
// // 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{}
}
说到底,Context接口就定义如下四个方法:
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
Context的继承衍生
context包为我们提供的With系列的函数了
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
这四个With函数,接收的都有一个partent参数,就是父Context,我们要基于这个父Context创建出子Context的意思
WithCancel源代码:
// 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.
/*
WithCancel 返回带有新 Done channel 的父级副本。
当调用返回的取消函数或父context的 Done channel关闭时,
返回的context的 Done 通道关闭.或者当父context的 Done 通道关闭时,以先发生者为准。
*/
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
// 当操作context结束后应该尽快调用cancel以释放资源
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
// 传入的父context不能是空(也就是已经关闭的通道是不行的,nil也是不行的)
if parent == nil {
panic("cannot create context from nil parent")
}
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
看一下这个语句返回的c:
c := newCancelCtx(parent)
// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
return cancelCtx{Context: parent}
}
type cancelCtx struct {
Context
mu sync.Mutex // protects following fields
done chan struct{} // created lazily, closed by first cancel call
children map[canceler]struct{} // set to nil by the first cancel call
err error // set to non-nil by the first cancel call
}
再看一下这个函数的实现:propagateCancel(parent, &c)
// propagateCancel arranges for child to be canceled when parent is.
// propagateCancel 安排父亲的子context取消
func propagateCancel(parent Context, child canceler) {
// 父done
done := parent.Done()
// 如果done为nil就说明父context永远不会被取消
if done == nil {
return // parent is never canceled
}
select {
case <-done:
// parent is already canceled
child.cancel(false, parent.Err())
return
default:
}
if p, ok := parentCancelCtx(parent); ok {
p.mu.Lock()
if p.err != nil {
// parent has already been canceled
child.cancel(false, p.err)
} else {
if p.children == nil {
p.children = make(map[canceler]struct{})
}
p.children[child] = struct{}{}
}
p.mu.Unlock()
} else {
atomic.AddInt32(&goroutines, +1)
go func() {
select {
case <-parent.Done():
child.cancel(false, parent.Err())
case <-child.Done():
}
}()
}
}
看一下CancelFunc的定义和上述返回:
type CancelFunc func()
// 返回的cancel函数:
func() {
c.cancel(true, Canceled)
}
// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
/*
cancel 关闭 c.done,取消 c 的每个孩子,如果
removeFromParent 为真,从其父级的子级中删除 c
*/
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
if c.done == nil {
c.done = closedchan
} else {
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)
}
}