Golang之Context

目录

 

1、Context接口:

    1.1、四个方法:

        1.1.1、Deadline方法:

        1.1.2、Done方法:

        1.1.3、Err方法:

        1.1.4、Value方法:

2、Context接口实现:

    2.1、Background方法:

    2.2、TODO方法:

3、context类型:

    3.1、valueCtx:

        3.1.1、

        3.1.2、

        3.1.3、

    3.2、cancelCtx:

        3.2.1、

        3.2.2、

    3.3、timerCtx:

        3.3.1、

4、相关函数:

    4.1、WithValue:

    4.2、WithCancel:

    4.3、WithDeadline:

    4.4、WithTimeout:


1、Context接口:

    1.1、四个方法:

        1.1.1、Deadline方法:

            释义:返回绑定当前context的任务被取消的截止时间,如果没有设定期限,将返回ok == false

            作用:指示一段时间后当前goroutine是否会被取消

        1.1.2、Done方法:

            释义:当绑定当前context的任务被取消时,将返回一个关闭的channel;如果当前context不会被取消,将返回nil

            作用:返回的channel正是用来传递结束信号以抢占并中断当前任务

        1.1.3、Err方法:

            释义:如果Done返回的channel没有关闭,将返回nil;如果Done返回的channel已经关闭,将返回非空的值表示任务结束的原因。如果是context被取消,Err将返回Canceled;如果是context超时,Err将返回DeadlineExceeded

            作用:解释goroutine被取消的原因

        1.1.4、Value方法:

            释义:返回context存储的键值对中当前key对应的值,如果没有对应的key,则返回nil

            作用:用于获取特定于当前任务树的额外信息

2、Context接口实现:

    一个int类型的变量emptyCtx,没有超时时间,不能取消,也不能存储任何额外信息,emptyCtx作为context树的根节点

    一般不会直接使用emptyCtx,而是使用emptyCtx实例化的两个变量,通过调用Background和TODO方法得到

    2.1、Background方法:

    通常被用于主函数、初始化以及测试中,作为一个顶层的context,即一般创建的context都是基于Background

    2.2、TODO方法:

    不确定使用什么context的时候才会使用

3、context类型:

    3.1、valueCtx:

type valueCtx struct {
    Context
    key, val interface{}
}

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

        3.1.1、

        利用一个Context类型的变量来表示父节点context,所以当前context继承了父context的所有信息;

        3.1.2、

        valueCtx类型还携带一组键值对,也就是说这种context可以携带额外的信息。

        3.1.3、

        valueCtx实现了value方法,用以在context链路上获取key对应的值,如果当前context上不存在需要的key,会沿着context链向上寻找key对应的值,直到根节点

    3.2、cancelCtx:

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
}

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

        3.2.1、

        跟valueCtx类似,cancelCtx中也有一个context变量作为父节点;

        3.2.2、

        实现了cancel方法,调用时会设置取消原因,将done channel设置为一个关闭channel或者关闭channel,然后将子节点context依次取消,如果有需要还会将当前节点从父节点上移除

    3.3、timerCtx:

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
}

func (c *timerCtx) cancel(removeFromParent bool, err error) {
    将内部的cancelCtx取消
    c.cancelCtx.cancel(false, err)
    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()
}

        3.3.1、

        基于cancelCtx的context类型,可以定时取消的context

4、相关函数:

    4.1、WithValue:

func WithValue(parent Context, key, val interface{}) Context {
    if key == nil {
        panic("nil key")
    }
    if !reflect.TypeOf(key).Comparable() {
        panic("key is not comparable")
    }
    return &valueCtx{parent, key, val}
}

    用以向context添加键值对,注意不是在原context结构体上直接添加,而是以此context作为父节点,重新创建一个新的valueCtx子节点,将键值对添加在子节点上,由此形成一条context链。获取value的过程就是在这条context链上由尾部向上搜寻

    4.2、WithCancel:

type CancelFunc func()

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
    c := newCancelCtx(parent)
    propagateCancel(parent, &c)
    return &c, func() { c.cancel(true, Canceled) }
}

// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
    // 将parent作为父节点context生成一个新的子节点
    return cancelCtx{Context: parent}
}

func propagateCancel(parent Context, child canceler) {
    if parent.Done() == nil {
        // parent.Done()返回nil表明父节点以上的路径上没有可取消的context
        return // parent is never canceled
    }
    // 获取最近的类型为cancelCtx的祖先节点
    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{})
            }
            // 将当前子节点加入最近cancelCtx祖先节点的children中
            p.children[child] = struct{}{}
        }
        p.mu.Unlock()
    } else {
        go func() {
            select {
            case <-parent.Done():
                child.cancel(false, parent.Err())
            case <-child.Done():
            }
        }()
    }
}

func parentCancelCtx(parent Context) (*cancelCtx, bool) {
    for {
        switch c := parent.(type) {
        case *cancelCtx:
            return c, true
        case *timerCtx:
            return &c.cancelCtx, true
        case *valueCtx:
            parent = c.Context
        default:
            return nil, false
        }
    }
}

    用来创建一个可以取消的context,即cancelCtx类型的context,返回一个context和一个CancelFunc,调用CancelFunc触发cancel操作

    4.3、WithDeadline:

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
    if cur, ok := parent.Deadline(); ok && cur.Before(d) {
        // The current deadline is already sooner than the new one.
        return WithCancel(parent)
    }
    c := &timerCtx{
        cancelCtx: newCancelCtx(parent),
        deadline:  d,
    }
    // 建立新建context与可取消context祖先节点的取消关联关系
    propagateCancel(parent, c)
    dur := time.Until(d)
    if dur <= 0 {
        c.cancel(true, DeadlineExceeded) // deadline has already passed
        return c, func() { c.cancel(false, Canceled) }
    }
    c.mu.Lock()
    defer c.mu.Unlock()
    if c.err == nil {
        c.timer = time.AfterFunc(dur, func() {
            c.cancel(true, DeadlineExceeded)
        })
    }
    return c, func() { c.cancel(true, Canceled) }
}

    返回一个基于parent的可取消的context,并且过期时间deadline不晚于设置时间d

    4.4、WithTimeout:

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
    return WithDeadline(parent, time.Now().Add(timeout))
}

    与WithDeadline类似,WithTimeout也是创建一个定时取消的context,只不过WithDeadline是接收一个过期时间点,而WithTimeout接收一个相对当前时间的过期时长timeout

你可能感兴趣的:(Golang,golang,后端)