1.context接口说明
type Context interface {
//返回过期时间,在timerCtx中生效
Deadline() (deadline time.Time, ok bool)
//返回标识ctx是否结束的channel
Done() <-chan struct{}
//返回错误
Err() error
//返回ctx存放的key-value对
Value(key any) any
}
在golang的context包中,有emptyCtx、cancelCtx、timerCtx、valueCtx都实现了上述context接口。
2.全局Err
//context被cancel时的错误
var Canceled = errors.New("context canceled")
//context超时时的错误
var DeadlineExceeded error = deadlineExceededError{}
type deadlineExceededError struct{}
func (deadlineExceededError) Error() string { return "context deadline exceeded" }
func (deadlineExceededError) Timeout() bool { return true }
func (deadlineExceededError) Temporary() bool { return true }
3.emptyCtx
1.emptyCtx是一个空context,本质是一个整型,context.Background()、context.TODO()均是返回此类型
type emptyCtx int
//没有过期时间
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
//如果往chan写入/读取数据,均会阻塞
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
//没有err
func (*emptyCtx) Err() error {
return nil
}
//没有value
func (*emptyCtx) Value(key any) any {
return nil
}
func (e *emptyCtx) String() string {
switch e {
case background:
return "context.Background"
case todo:
return "context.TODO"
}
return "unknown empty Context"
}
2.background、todo
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
//返回一个非nil的emptyCtx,不会被cancel,没有value,没有deadline
//可用在main中,作为context链路中最顶层的父context
func Background() 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).
func TODO() Context {
return todo
}
4.cancler 接口
//具备cancel能力,cancelCtx与timerCtx实现了此接口
type canceler interface {
cancel(removeFromParent bool, err, cause error)
Done() <-chan struct{}
}
5.cancelCtx
5.1 cancelCtx定义
//用于表示cancelCtx返回自身而约定的key
var cancelCtxKey int
type cancelCtx struct {
Context // 继承context,作为当前cancelCtx的父context
mu sync.Mutex // protects following fields
done atomic.Value // of 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
cause error // set to non-nil by the first cancel call
}
func (c *cancelCtx) Value(key any) any {
if key == &cancelCtxKey {
return c
}
return value(c.Context, key)
}
func (c *cancelCtx) Done() <-chan struct{} {
d := c.done.Load()
if d != nil {
return d.(chan struct{})
}
c.mu.Lock()
defer c.mu.Unlock()
d = c.done.Load()
if d == nil {
d = make(chan struct{})
c.done.Store(d)
}
return d.(chan struct{})
}
func (c *cancelCtx) Err() error {
c.mu.Lock()
err := c.err
c.mu.Unlock()
return err
}
type stringer interface {
String() string
}
func contextName(c Context) string {
if s, ok := c.(stringer); ok {
return s.String()
}
return reflectlite.TypeOf(c).String()
}
func (c *cancelCtx) String() string {
return contextName(c.Context) + ".WithCancel"
}
// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
// cancel sets c.cause to cause if this is the first time c is canceled.
func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {
//主动cancel,err类型为Canceled
//父context cancel导致的cancel,err是父context的err
//所以err为nil,就是发生了错误,则panic
if err == nil {
panic("context: internal error: missing cancel error")
}
if cause == nil {
cause = err
}
c.mu.Lock()
//c.err != nil为什么会是already canceled? 有可能是:
//当我们执行c, cancel := context.WithCancel(context.Background())时,
//返回值cancel是CancelFunc类型,内容为func() { c.cancel(true, Canceled, nil) }
//调用cancel(),err是Canceled,cause为nil,主动cancel
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
c.err = err
c.cause = cause
d, _ := c.done.Load().(chan struct{})
if d == nil {
c.done.Store(closedchan)
} else {
close(d)
}
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err, cause)
}
c.children = nil
c.mu.Unlock()
//是否需要把当前cancelCtx从父context的children map中移除
if removeFromParent {
//在newCancelCtx(parent Context) *cancelCtx时,会将参数parent赋给c.Context
removeChild(c.Context, c)
}
}
// removeChild removes a context from its parent.
func removeChild(parent Context, child canceler) {
//判断parent是否是cancelCtx,只有cancelCtx才有children map
p, ok := parentCancelCtx(parent)
if !ok {
return
}
p.mu.Lock()
if p.children != nil {
delete(p.children, child)
}
p.mu.Unlock()
}
5.2 构造CancelCtx实例经历了哪些步骤?
WithCancel() --> withCancel() --> newCancelCtx() --> propagateCancel() --> parentCancelCtx()
//当某个具备cancel的context需要被cancel时,可以调用此方法
//此方法会在cancelCtx、timerCtx构造时返回
type CancelFunc func()
//1.构造CancelCtx时,首先会调用WithCancel()方法,同时返回cancelCtx与一个CancelFunc的方法
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := withCancel(parent)
return c, func() { c.cancel(true, Canceled, nil) }
}
func withCancel(parent Context) *cancelCtx {
//检验父context非空
if parent == nil {
panic("cannot create context from nil parent")
}
//基于父context,构造出新的子context
c := newCancelCtx(parent)
//传递cancel事件,父context cancel时,子context也会cancel
propagateCancel(parent, c)
return c
}
// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) *cancelCtx {
return &cancelCtx{Context: parent}
}
// propagateCancel用于传递父子context之间的cancel事件
//父context被cancel时,子context也会被cancel
//cancelCtx是canceler的实现类
func propagateCancel(parent Context, child canceler) {
done := parent.Done()
//判断parent是否是可以被cancel的类型
if done == nil {
//emptyCtx是不会被cancel的类型
return // parent is never canceled
}
//如果当前parent被cancel,则子context直接也被cancel
select {
case <-done:
// parent is already canceled
child.cancel(false, parent.Err(), Cause(parent))
return
default:
}
//判断:如果parent是cancelCtx类型,将子context加入parent的cchildren map[canceler]struct{} 当中
if p, ok := parentCancelCtx(parent); ok {
//向parent的cchildren map[canceler]struct{} 当中写数据加写锁
p.mu.Lock()
if p.err != nil {
// parent has already been canceled
child.cancel(false, p.err, p.cause)
} else {
if p.children == nil {
p.children = make(map[canceler]struct{})
}
p.children[child] = struct{}{}
}
p.mu.Unlock()
} else {
//parent不是cancelCtx类型,但是又存在cancel的能力(在此方法开头第二行就判断了parent是否是可以被cancel的类型)
//启动一个协程,通过多路复用的方式监控parent的状态,如果parent终止,则同时终止子context,
//但如果子context终止,是与父context也就是parent无关的
goroutines.Add(1)
go func() {
select {
case <-parent.Done():
child.cancel(false, parent.Err(), Cause(parent))
case <-child.Done():
}
}()
}
}
//判断父context是否是*cancelCtx类型
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
done := parent.Done()
//parent已经被关闭或者是不会被cance的类型
if done == closedchan || done == nil {
return nil, false
}
//*cancelCtx的Value()方法会比较参数key是否和&cancelCtxKey相同并返回,
//如果相同就会返回*cancelCtx本身,可以再次被断言成*cancelCtx
// func (c *cancelCtx) Value(key any) any {
// if key == &cancelCtxKey {
// return c
// }
// return value(c.Context, key)
// }
//此时传递的参数就是&cancelCtxKey,如果parent指向的是*cancelCtx类型,ok==true
p, ok := parent.Value(&cancelCtxKey).(*cancelCtx)
if !ok {
return nil, false
}
//进一步判断*cancelCtx是否被用户自定义实现过
pdone, _ := p.done.Load().(chan struct{})
if pdone != done {
return nil, false
}
//返回*cancelCtx,true
return p, true
}
思考:当程序执行context.WithCancel(context.Background)时发生了什么?
6.timerCtx
6.1 timerCtx类定义
type timerCtx struct {
*cancelCtx //在cancelCtx的基础上,增加超时功能
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}
//context接口的DeadLine()仅在timerCtx中有效,用于返回过期时间
func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
return c.deadline, true
}
func (c *timerCtx) String() string {
return contextName(c.cancelCtx.Context) + ".WithDeadline(" +
c.deadline.String() + " [" +
time.Until(c.deadline).String() + "])"
}
func (c *timerCtx) cancel(removeFromParent bool, err, cause error) {
//调用继承的cancelCtx的cancel
c.cancelCtx.cancel(false, err, cause)
if removeFromParent {
// Remove this timerCtx from its parent cancelCtx's children.
removeChild(c.cancelCtx.Context, c)
}
c.mu.Lock()
if c.timer != nil {
c.timer.Stop()
c.timer = nil
}
c.mu.Unlock()
}
// removeChild removes a context from its parent.
func removeChild(parent Context, child canceler) {
//判断parent是否是cancelCtx,只有cancelCtx才有children map
p, ok := parentCancelCtx(parent)
if !ok {
return
}
p.mu.Lock()
if p.children != nil {
delete(p.children, child)
}
p.mu.Unlock()
}
6.2 构造timerCtx经厉了哪些步骤?
//我们常用的带有超时功能的context,其实也是调用的WithDeadline()方法
//不过WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
//d:context在d到来的时刻过期
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
if parent == nil {
panic("cannot create context from nil parent")
}
//判断parent是否是timerCtx并且过期时间是否早于自己
if cur, ok := parent.Deadline(); ok && cur.Before(d) {
// The current deadline is already sooner than the new one.
return WithCancel(parent)
}
//构造新的*timerCtx
c := &timerCtx{
cancelCtx: newCancelCtx(parent),
deadline: d,
}
//传递父子context之间的cancel事件
//但是如果parent是emptyCtx,是没有cancel能力的,此时子context是不能通过父context终止的
propagateCancel(parent, c)
//过期时间与当前时间的差值
dur := time.Until(d)
if dur <= 0 {
//过期时间已到,cancel timerCtx,返回DeadlineExceeded类型错误
c.cancel(true, DeadlineExceeded, nil) // deadline has already passed
return c, func() { c.cancel(false, Canceled, nil) }
}
//加锁,启动timer,当timer时间到了,cancel timerCtx,返回DeadlineExceeded类型错误
//即便parent是empytCtx,也可在timer到期时被自动cancel,或者调用cancelFunc cancel
c.mu.Lock()
defer c.mu.Unlock()
if c.err == nil {
c.timer = time.AfterFunc(dur, func() {
c.cancel(true, DeadlineExceeded, nil)
})
}
return c, func() { c.cancel(true, Canceled, nil) }
}
7.valueCtx
7.1valueCtx类定义
type valueCtx struct {
Context
key, val any
}
// 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().
func stringify(v any) string {
switch s := v.(type) {
case stringer:
return s.String()
case string:
return s
}
return "<not Stringer>"
}
func (c *valueCtx) String() string {
return contextName(c.Context) + ".WithValue(type " +
reflectlite.TypeOf(c.key).String() +
", val " + stringify(c.val) + ")"
}
func (c *valueCtx) Value(key any) any {
if c.key == key {
return c.val
}
return value(c.Context, key)
}
func value(c Context, key any) any {
for {
//ctx := c.(type)类型判断,判断接口是否是指定类型,
//从而做出不同的行为,type-switch是固定写法,多态的一种表现形式
switch ctx := c.(type) {
case *valueCtx:
//如果与当前valeCtx.key,返回value,否则继续向上寻找
if key == ctx.key {
return ctx.val
}
c = ctx.Context
case *cancelCtx:
//特定协议:如果key是&cancelCtxKey,返回*cancelCtx本身,否则继续向上寻找
if key == &cancelCtxKey {
return c
}
c = ctx.Context
case *timerCtx:
//timerCtx继承了*cancelCtx,所以判断key是不是&cancelCtxKey,
//是就返回timerCtx中继承的*cancelCtx即可,不是就继续向上寻找
if key == &cancelCtxKey {
return ctx.cancelCtx
}
c = ctx.Context
case *emptyCtx:
//找不到当前的key
return nil
default:
//其他自定义实现context接口的类型
return c.Value(key)
}
}
}
7.2 构造valueCtx时发生了什么?
实例化一个valueCtx还是比较简单的。
func WithValue(parent Context, key, val any) Context {
if parent == nil {
panic("cannot create context from nil parent")
}
if key == nil {
panic("nil key")
}
if !reflectlite.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return &valueCtx{parent, key, val}
}