핵심을 달성하기 위해 그래픽 언어 프로그래밍 언어의 소스 코드를 이해하기 위해 컨텍스트를 이동

재단은 기반을 구축하기 위해

디자인의 일부를 기반으로 스레드 프로그래밍 언어

쓰레드 그룹

의 image.png
스레드는 일반적으로 부모 스레드의 스레드 그룹에 추가 하위 스레드를 도출 할 때이 언어를 프로그래밍 동시 스레드의 일반적으로 사용되는 개념을 기반으로하는 ThreadGroup 마지막으로는 쓰레드 그룹에 의해 설정을 제어 할 수 있습니다 (스레드 그룹을 지정하지 않음) 현재 호출 체인에있는 모든 goroutine를 종료하려면 종료 스레드와 다른 작업이 부모 / 자녀 사이의 다음 이동 언어 goroutine 명확한 관계는, 사용 상황에 필요합니다

ThreadLocal를

스레드 기반의 프로그래밍 언어에서, 언어는 일반적으로 일부 스레드 로컬 저장소를 수행하기 위해 ThreadLocal를 기반으로 할 수 있습니다, 자연과 이동의지도를 통해 키 / 값 저장하는 것입니다, 키 / 값 전달 더 ThreadLocal를 디자인, 없다 매개 변수 이외에 통과 할 때, 그것은 컨텍스트 전송 콘텍스트 정보에 의해 수행 될 수있다

상황에 맞는 일반적인 응용 프로그램 시나리오

장면 실현 원칙
문맥 정보 전송 WithValue 수정 될 수없는 내부의 키 / 값의 특성을 통해 키 쌍을 저장하기 만의 커버 가치로 대체 될 수있다
철수의 공지 사항 WithCancel 통지를 수신하여 공통 출구 채널을 통보합니다

재귀 컨텍스트 데이터 취득

의 image.png
맥락에서 아무 소용지도 데이터 내에서 갈이 있기 때문에 이렇게하면 실제 인수가 일치하는 키를 찾을 때까지, 현재 레이어의 시작 부분에서 레이어로 재귀 접속 층이다, 저장하기

goroutine 자체 개념적 종속하지 않지만, 실제로 우리는 부모 - 자식 관계의 컨텍스트를 통한 데이터 전송을 달성 할 수 있기 때문에, 사실, 우리는 유사 쓰레드 그룹, 데이터 도출 goroutine에 컨텍스트 goroutine 설정하고 전달할 수

취소 통지

의 image.png
既然通过context来构建parent/child的父子关系,在实现的过程中context会向parent来注册自身,当我们取消某个parent的goroutine, 实际上上会递归层层cancel掉自己的child context的done chan从而让整个调用链中所有监听cancel的goroutine退出

那如果一个child context的done chan为被初始化呢?那怎么通知关闭呢,那直接给你一个closedchan已经关闭的channel那是不是就可以了呢

带有超时context

의 image.png
如果要实现一个超时控制,通过上面的context的parent/child机制,其实我们只需要启动一个定时器,然后在超时的时候,直接将当前的context给cancel掉,就可以实现监听在当前和下层的额context.Done()的goroutine的退出

Background与TODO

의 image.png
Backgroud其实从字面意思就很容易理解,其实构建一个context对象作为root对象,其本质上是一个共享的全局变量,通常在一些系统处理中,我们都可以使用该对象作为root对象,并进行新context的构建来进行上下文数据的传递和统一的退出控制

那TODO呢?通常我们会给自己立很多的todo list,其实这里也一样,我们虽然构建了很多的todo list, 但大多数人其实啥也不会做,在很多的函数调用的过程中都会传递但是通常又不会使用,比如你既不会监听退出,也不会从里面获取数据,TODO跟Background一样,其背后也是返回一个全局变量

不可变性

通常我们使用context都是做位一个上下文的数据传递,比如一次http request请求的处理,但是如果当这次请求处理完成,其context就失去了意义,后续不应该继续重复使用一个context, 之前如果超时或者已经取消,则其状态不会发生改变

源码实现

context接口

type Context interface {
    // Deadline返回一个到期的timer定时器,以及当前是否以及到期
    Deadline() (deadline time.Time, ok bool)

    // Done在当前上下文完成后返回一个关闭的通道,代表当前context应该被取消,以便goroutine进行清理工作
    // WithCancel:负责在cancel被调用的时候关闭Done
    // WithDeadline: 负责在最后其期限过期时关闭Done
    // WithTimeout:负责超时后关闭done
    Done() <-chan struct{}

    // 如果Done通道没有被关闭则返回nil
    // 否则则会返回一个具体的错误
    // Canceled 被取消
    // DeadlineExceeded 过期
    Err() error
    // 返回对应key的value
    Value(key interface{}) interface{}
}

emptyCtx

emptyCtx是一个不会被取消、没有到期时间、没有值、不会返回错误的context实现,其主要作为context.Background()和context.TODO()返回这种root context或者不做任何操作的context

type emptyCtx int

func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
    return
}

func (*emptyCtx) Done() <-chan struct{} {
    return nil
}

func (*emptyCtx) Err() error {
    return nil
}

func (*emptyCtx) Value(key interface{}) interface{} {
    return nil
}
func (e *emptyCtx) String() string {
    switch e {
    case background:
        return "context.Background"
    case todo:
        return "context.TODO"
    }
    return "unknown empty Context"
}

比较有意思的实现时emptyCtx的String方法,该方法可以返回当前context的具体类型,比如是Background还是TODO, 因为background和todo是两个全局变量,这里通过取其地址来进行对应类型的判断

cancelCtx

의 image.png

结构体

cancelCtx结构体内嵌了一个Context对象,即其parent context,同时内部还通过children来保存所有可以被取消的context的接口,后续当当前context被取消的时候,只需要调用所有canceler接口的context就可以实现当前调用链的取消

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
}

Done

Done操作返回当前的一个chan 用于通知goroutine退出


func (c *cancelCtx) Done() <-chan struct{} {
    c.mu.Lock()
    if c.done == nil {
        c.done = make(chan struct{})
    }
    d := c.done
    c.mu.Unlock()
    return d
}

cancel

func (c *cancelCtx) cancel(removeFromParent bool, err error) {
    if err == nil {
        panic("context: internal error: missing cancel error")
    }
    // context一旦被某个操作操作触发取消后,就不会在进行任何状态的修改
    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当前chan
        close(c.done)
    }
    // 调用所有children取消
    for child := range c.children {
        child.cancel(false, err)
    }
    c.children = nil
    c.mu.Unlock()

    // 是否需要从parent context中移除,如果是当前context的取消操作,则需要进行该操作
    // 否则,则上层context会主动进行child的移除工作
    if removeFromParent {
        removeChild(c.Context, c)
    }
}

timerCtx

의 image.png
timerCtx主要是用于实现WithDeadline和WithTimer两个context实现,其继承了cancelCtx接口,同时还包含一个timer.Timer定时器和一个deadline终止实现

2.4.1 结构体

timerCtx

type timerCtx struct {
    cancelCtx
    timer *time.Timer // timer定时器

    deadline time.Time //终止时间
}

取消方法

取消方法就很简单了首先进行cancelCtx的取消流程,然后进行自身的定时器的Stop操作,这样就可以实现取消了

func (c *timerCtx) cancel(removeFromParent bool, err error) {
    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()
}

valueCtx

其内部通过一个key/value进行值得保存,如果当前context不包含着值就会层层向上递归

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

func (c *valueCtx) String() string {
    return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
}

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

propagateCancel

设计目标

propagateCancel主要设计目标就是当parent context取消的时候,进行child context的取消, 这就会有两种模式:
1.parent取消的时候通知child进行cancel取消
2.parent取消的时候调用child的层层递归取消

parentCancelCtx

부모가 제 2 모드의 사용에, 한 번 timerCtx cancelCtx 때 컨텍스트를 조합하여 두 모드 위에 발견 될 수있다하는 N 층으로 이루어지는 임의로 중첩 문맥 트리 구조 일 수 있고, 아이의 부모가 통화를 취소 exit를 호출 체인을 완료하고, 그 반대는 반대의 완료를 듣는 첫 번째 모드를 사용하여

func parentCancelCtx(parent Context) (*cancelCtx, bool) {
    for {
        switch c := parent.(type) {
        case *cancelCtx:
            return c, true  // 找到最近支持cancel的parent,由parent进行取消操作的调用
        case *timerCtx:
            return &c.cancelCtx, true // 找到最近支持cancel的parent,由parent进行取消操作的调用
        case *valueCtx:
            parent = c.Context // 递归
        default:
            return nil, false
        }
    }
}

코어 구현

func propagateCancel(parent Context, child canceler) {
    if parent.Done() == nil {
        return // parent is never canceled
    }
    if p, ok := parentCancelCtx(parent); ok {
        p.mu.Lock()
        if p.err != nil {
            // parent has already been canceled
            // 如果发现parent已经取消就直接进行取消
            child.cancel(false, p.err)
        } else {
            if p.children == nil {
                p.children = make(map[canceler]struct{})
            }
            // 否则加入parent的children map中
            p.children[child] = struct{}{}
        }
        p.mu.Unlock()
    } else {
        go func() {
            select {
            case <-parent.Done():
                // 监听parent DOne完成, 此处也不会向parent进行注册
                child.cancel(false, parent.Err())
            case <-child.Done():
            }
        }()
    }
}

WithDeadline

위의 기본 학습 WithDeadline으로, 그것은 훨씬 쉽게, 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,
    }
    // 监听parent的取消,或者向parent注册自身
    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() {
            // 构建一个timer定时器,到期后自动调用cancel取消
            c.cancel(true, DeadlineExceeded)
        })
    }
    // 返回取消函数
    return c, func() { c.cancel(true, Canceled) }
}

Backgroup 与 ALL

많은 통화에서 기본 미들웨어에 그들이 emptyCtx을 기반으로하지만, 가장 일반적이고 Backgroup 할 일이다 컨텍스트 정보를 통과 달성,하지만 더 전체의 후속 호출 체인 컨텍스트에 대한 부모 컨텍스트로 Backgroup를 기울어 져 루트를 사용하고, 일반적으로 매개 변수의 사용이 전달 될 수 있기 때문에 단순히 후속 TODO이 모든 작업을 수행 할 것을 나타냅니다

원래 링크 http://www.sreguide.com/go/context.html
마이크로 신호 : baxiaoshi2020의
기사의 우려 게시판 번 더 읽기 소스 코드 분석 이십일일 온실
기사 더 우려 www.sreguide.com
블로그 기사의 문서, 여러 플랫폼 OpenWrite 출시

추천

출처www.cnblogs.com/buyicoding/p/12155169.html