Go language context (design and analysis)

Source: Internet
Author: User
Tags comparable sprintf

A simple overview of the context:

Each request from the GO server has its own goroutine, and some requests to improve performance often initiate additional goroutine processing requests, and when the request is canceled or timed out, all goroutines on the request should exit to prevent resource leaks. Then the context comes, it constrains all goroutines on the request, and then cancels the signal, timeouts, and so on.

and the context advantage is concise management goroutines life cycle.

Context Simple to use:

Next simulate a timeout to continue analyzing the context

注意: 使用时遵循context规则1. 不要将 Context放入结构体,Context应该作为第一个参数传   入,命名为ctx。2. 即使函数允许,也不要传入nil的 Context。如果不知道用哪种    Context,可以使用context.TODO()。3. 使用context的Value相关方法,只应该用于在程序和接口中传递   和请求相关数据,不能用它来传递一些可选的参数4. 相同的 Context 可以传递给在不同的goroutine;Context 是   并发安全的。

Use Net/http/pprof to view the Goroutines:

  package Mainimport ("context" "FMT" "Net/http" _ "Net/http/pprof" "Time" ) Func main () {go HTTP. Listenandserve (": 8080", nil) ctx, _: = Context. Withtimeout (context. Background (), (Ten * time. Second)) Go TestA (ctx) Select {}}func TestA (CTX context). Context) {Ctxa, _: = Context. Withtimeout (CTX, (5 * time). Second) ch: = make (chan int) go testb (CTXA, ch) Select {Case <-ctx. Done (): FMT. Println ("TestA done") return case I: = <-ch:fmt. Println (i)}}func Testb (CTX context. Context, ch Chan int) {//analog read data sumch: = make (chan int) go func (sumch chan int) {sum: = TIME.S Leep (Ten * time. Second) sumch <-sum} (SUMCH) Select {Case <-ctx. Done (): FMT.         Println ("Testb done") <-sumch return//case ch <-<-sumch: Note This causes a resource leak case I: = <-sumch: Fmt. PRINTLN ("Send", i) ch <-i}}  

Analog database read efficiency is slow

From the execution and execution of the results, we perfectly closed the unwanted goroutine,


Timeout

The overview mentions:
When the application scenario is derived from a request of multiple goroutine completion requirements, then they need to meet a certain constraint relationship, in order to abort the routine tree, timeout and other operations.
So how do you get to the constraint relationship?
Simply understood, the invocation of the context exists as a chain, deriving a new context from the current parent context through the Withxxx method, and when the parent context is canceled, all of its derived context is canceled.


Withcancel derived constraint graphs

Withcancel derivation simple analysis diagram, next we conduct source analysis, further understand the context of the constraint relationship (Withxxx method derivation is probably the same as Withcancel).

Context Source Analysis:
type Context interface {    Deadline() (deadline time.Time, ok bool)    Done() <-chan struct{}    Err() error    Value(key interface{}) interface{}}

Deadline () returns the time. Time it is the end of the context, OK indicates whether there is a deadline
Done () returns a channel that is closed when the context is revoked or expires, and can be simply considered a shutdown signal.
Err () When the done channel is closed, ERR returns the reason for the shutdown (such as timeout, manual shutdown)

Value (Key interface{}) a method for k-v storage

Canceler provides the cancal function while requiring a data structure to implement the context

type canceler interface {    cancel(removeFromParent bool, err error)    Done() <-chan struct{}}
//默认错误var Canceled = errors.New("context canceled")var DeadlineExceeded = errors.New("context deadline exceeded")

Implementing the context data structure

type emptyCtx intfunc (*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"}

Two empty structures that implement the context.

var (    background = new(emptyCtx)    todo       = new(emptyCtx))func Background() Context {    return background}func TODO() Context {    return todo}

The CANCELCTX structure inherits the context and implements the Canceler interface:

Type cancelctx struct {Context done chan struct{}//Closed by the first cancel call. Mu sync. Mutex children Map[canceler]bool//set to nil by the first cancel call err Error//When cancel will put ER     R set to non-Nil}func (c *cancelctx) done () <-chan struct{} {return C.done}func (c *cancelctx) ERR () error {C.mu.lock () Defer C.mu.unlock () return C.err}func (c *cancelctx) string () string {return FMT.        Sprintf ("%v.withcancel", C.context)}func (c *cancelctx) cancel (removefromparent bool, err error) {if Err = = Nil { Panic ("Context:internal error:missing Cancel Error")} c.mu.lock ()//timerctx will use this code frequently because it derives//timer    CTX all points to the same cancelctx. If C.err! = nil {C.mu.unlock () return//already canceled} C.err = err//off C.done Close (c.done )//Traverse C.children, each child is cancel 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 is true, remove C from the children of its parent if removefromparent {REM Ovechild (C.context, c)}}//If the parent is a valuectx type, it loops over the nearest parent//for the Cancelctx type, and finds the child from the children//map of the parent object, Otherwise, nil is returned (context. Background or context.     TODO) Func removechild (parent Context, child Canceler) {p, OK: = Parentcancelctx (parent) if!ok {return} P.mu.lock () if p.children! = nil {Delete (P.children, Child)} P.mu.unlock ()}

Next analysis of the cancel related code

The Type Cancelfunc func ()//withcancel method returns a context pair//image that inherits the parent, while the Cancel method returned can be used to close the done channelfunc in the current//context Withcancel (parent context) (CTX context, cancel Cancelfunc) {c: = Newcancelctx (parent) propagatecancel (parent, &amp ; c) return &c, func () {C.cancel (True, Canceled)}}func newcancelctx (parent Context) Cancelctx {return CANCELCT x{context:parent, Done:make (Chan struct{}),}}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 {//If the found parent has been cancel,//Then the child tree passed in will be cancel Child.cancel (False, P.err)} else {//Otherwise, the child node is added directly to the parent's children//(so there is a constraint, up             The father pointer does not change//, the child pointer down can be used directly) if P.children = = Nil {P.children = make (Map[canceler]bool) } p.children[child] = struct{}{}} p.mu.unlock ()} else {//if the nearest parent that can be cancel is not found,//starts a goroutine, waiting for incoming pare      NT terminates,//and cancel the incoming child tree, or waits for the incoming child to terminate. Go func () {select {case <-parent. Done (): Child.cancel (False, parent.) ERR ()) Case <-child.    Done ():}} ()}}//determines whether the parent is a Cancelctx type 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}}}

Timerctx inherits the structure of Cancelctx, and this design avoids writing duplicate code and improves reuse

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) String() string {    return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))}// 与cencelCtx有所不同,除了处理cancelCtx.cancel,// 还回对c.timer进行Stop(),并将c.timer=nilfunc (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()}

Timerctx specific two methods:

Func Withdeadline (parent Context, Deadline time. Time) (Context, Cancelfunc) {//If the parent's deadline is earlier than the newly-passed deadline, the Withcancel is returned directly//, because the parent's deadline will first expire, and the new//DEADL INE according to not need if cur, OK: = parent. Deadline (); OK && cur.        Before (deadline) {//The current deadline is already sooner than the new one.    return Withcancel (Parent)} c: = &timerctx{cancelctx:newcancelctx (parent), Deadline:deadline, } propagatecancel (parent, c)//check if it has expired, then cancel new child tree D: = deadline. Sub (time. Now ()) if D <= 0 {C.cancel (true, deadlineexceeded)//deadline have already passed return C, func () { C.cancel (True, Canceled)}} c.mu.lock () defer c.mu.unlock () if C.err = nil {//without being cancel, set Deadlin E The timer for cancel after C.timer = time. Afterfunc (D, func () {C.cancel (True, Deadlineexceeded)})} return C, func () {C.cancel (True, Canc eled)}}//withtimeout simple violence, direct withtimeout back to Func withtimeout (Parent Context, timeout time. Duration) (Context, Cancelfunc) {return Withdeadline (parent, time. Now (). ADD (timeout))}

Valuectx is primarily used to pass some comparable operational data

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}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)}

Go language context (design and analysis)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.