Golang Context Analysis

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

[TOC]

Golang Context Analysis

Context background and applicable scenarios

Golang in the 1.6.2 of the time did not have their own context, in the 1.7 version of the Golang.org/x/net/context package was added to the official library. Golang's context package is specifically designed to simplify the processing of data, cancellation signals, cutoff times, and so on, between multiple goroutine that handle a single request, and may involve multiple API calls.

For example, there is a request for network requests, each request needs to open a goroutine to do something, these goroutine may open other goroutine. In this way, we can track these goroutine through the context, and control their purpose through the context, which is the context that the go language provides for us, and Chinese can be called "contexts".

Another practical example is that in the Go Server program, each request will have a goroutine to handle. However, handlers often need to create additional goroutine to access back-end resources, such as databases, RPC services, and so on. Because these goroutine are all processing the same request, they often require access to some shared resources, such as user identity information, authentication tokens, request deadlines, and so on. And if the request times out or is canceled, all goroutine should exit immediately and release the associated resources. This situation also needs to use context to cancel out all goroutine for us.

If you want to use the go get golang.org/x/net/context command to get this package.

Context definition

Ontext's main data structure is a nested structure or a one-way inheritance structure, such as the original context is a small box, containing some data, Then the children inherited from this context is like a box in the original context, and then contains some of its own data. Or the context is a hierarchical structure, depending on the use of the scene, each layer of context has a number of different features, this hierarchical organization also makes the context easy to expand, clear responsibilities.

The core of the context package is the struct context, which declares the following:

type Context interface {Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key interface{}) interface{}}

You can see that the context is a interface, in Golang, interface is a very broad structure that can accommodate any type. The context definition is very simple, altogether 4 methods, we need to be able to understand these several methods well

    1. The deadline method is to get the set deadline meaning, the first return is the cutoff time, to this point in time, the context will automatically initiate the cancellation request, the second return value Ok==false when there is no set deadline, if canceled, you need to call the cancellation function to cancel.

    2. The Done method returns a read-only Chan with a type of struct{}, and in Goroutine, if the method returned by Chan can read, it means that the parent context has initiated a cancellation request, and after we receive this signal through the Done method, You should do cleanup, then exit Goroutine and release resources. After that, the Err method returns an error telling you why the Context was canceled.

    3. The Err method returns the reason for canceling the error because what context was canceled.

    4. The value method gets the values bound by the context, which is a key-value pair, so a key can be used to obtain the corresponding value, which is generally thread-safe.

How to implement Context

Although the context is an interface, but does not need to implement the user, Golang built-in context package, has helped us to implement 2 methods, generally in the code, the beginning of the context is the two top-level parent context, And then derive the sub-context. These context objects form a tree: When a context object is canceled, all the context that inherits from it is canceled. The two implementations are as follows:

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

One is background, which is mainly used in the main function, initialization and test code, as the context of the top-level context of the tree structure, that is, the root context, it cannot be canceled.

One is TODO, we can use this if we don't know what context to use, but in practice, this todo has not been used for the time being.

Both of them are essentially emptyctx struct types, which is a context that cannot be canceled, has no set deadline, and does not carry any value.

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}

The inheritance of Context

With the root context, how do you derive more sub-context? This will depend on the context package for us to provide the function of the WITH series.

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 these functions, a context tree is created, each node of the tree can have any number of child nodes, and the node hierarchy can have any number of nodes.

The Withcancel function, passing a parent context as a parameter, returns a child context, and a cancellation function is used to cancel the context.

Withdeadline function, similar to Withcancel, it will pass a cutoff time parameter, meaning that at this point in time, will automatically cancel the context, of course, we can not wait until this time, can be canceled in advance by canceling the function.

Withtimeout and Withdeadline Basically the same, this means that the timeout is automatically canceled, is how much time after the automatic cancellation of the meaning of the context.

The Withvalue function has nothing to do with canceling the context, it is to generate a context that binds a key-value pair of data, This binding data can be accessed through the Context.value method, which is the technique we use often, which we want to pass through the context, such as when we need Tarce to trace the system call stack.

Detailed functions with series

Withcancel

Context. Withcancel generates an instance of Withcancel and a cancelfuc that is used to close the Done channel function in Ctxwithcancel.

Below to analyze the implementation of the source code, first look at the initialization, as follows:

func newCancelCtx(parent Context) cancelCtx {    return cancelCtx{        Context: parent,        done:    make(chan struct{}),    }}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,cancelctx struct that inherits the context and implements the Canceler method:

Both the *cancelctx and *timerctx implement the Canceler interface, and the type of implementation of the interface can be directly Canceledtype Canceler interface {cancel (removefromparent bool, E RR error) Done () <-chan struct{}}type cancelctx struct {Context do chan struct{}//Closed by the first can    Cel call. Mu sync. Mutex children Map[canceler]bool//set to nil by the first cancel call err Error//When it is cancel will put E The RR is 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)}//core is off c.done//also set c.err = err, C.children = nil// Iterate through C.children, each child cancel//if Removefromparent is set, then C removes the Func (c *cancelctx) cancel from the children of its parent (    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 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)//From here you can see that the CANCELCTX context item is a concept similar to the parent}}

As you can see, all children exist in a map; the Done method returns the done channel in it, while the other cancel method closes the done channel and traverses down through the layer, closing the children channel. and removes the current Canceler from the parent.

Withcancel Initializes a cancelctx, the Propagatecancel method is executed, and finally a cancel function is returned.

The Propagatecancel method is defined as follows:

 //Propagatecancel arranges for child to being canceled when the parent Is.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 Child.cancel (FA            LSE, P.ERR)} else {if P.children = = Nil {P.children = make (map[canceler]struct{})            } P.children[child] = struct{}{}} p.mu.unlock ()} else {go func () { Select {Case <-parent. Done (): Child.cancel (False, parent.) ERR ()) Case <-child. Done ():}} ()}}  

The meaning of Propagatecancel is to pass cancel, starting with the current incoming parent (including the parent), looking up the nearest parent that can be cancel, if the found parent has been cancel, To cancel the child tree passed in, otherwise, the child node is connected directly to the children of the found parent (the context field is unchanged, that is, the parent pointer is not changed, but the downward pointer to the children is direct); If the nearest parent that can be cancel is not found, that is, it cannot be cancel, a goroutine is started waiting for the incoming parent to terminate, cancel the incoming child tree, or wait for the child to end.

Withdeadline

In the Withcancel based on the expansion, if the time to cancel after the operation, the specific operation process is basically consistent with withcancel, except that the timing of the Cancel function call is controlled by a timeout channel.

Context usage principles and techniques

    • Do not put the context in the structure, to be passed as a parameter, the parent context is generally background
    • You should pass the context as the first parameter to each function on the portal request and the export request link, placing the first one, and the variable names are all unified, such as CTX.
    • When passing a context to a function method, do not pass nil, otherwise the connection will be broken when the Tarce is traced.
    • The context's value-related method should pass the necessary data, and no data is used for this pass
    • The context is thread-safe and can be safely passed across multiple goroutine
    • You can pass a Context object to any number of gorotuine, and all Goroutine will receive a cancellation signal when it cancels the operation.

Examples of common methods of context

  1. Call the context done method to cancel

    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:        }    }}
  2. Through the context. Withvalue to pass the value

    func main() {    ctx, cancel := context.WithCancel(context.Background())    valueCtx := context.WithValue(ctx, key, "add value")    go watch(valueCtx)    time.Sleep(10 * time.Second)    cancel()    time.Sleep(5 * time.Second)}func watch(ctx context.Context) {    for {        select {        case <-ctx.Done():            //get value            fmt.Println(ctx.Value(key), "is cancel")            return        default:            //get value            fmt.Println(ctx.Value(key), "int goroutine")            time.Sleep(2 * time.Second)        }    }}
  3. The
  4. Time-out cancels the context. Withtimeout

      package Mainimport ("FMT" "Sync" "Time" "Golang.org/x/net/context") VAR (wg sync . Waitgroup) Func work (CTX context. Context) Error {defer WG. Done () for I: = 0; i < 1000; i++ {select {case <-time. After (2 * time. Second): FMT. Println ("Doing Some work", I)//we received the signal of cancelation in this channel case <-ctx. Done (): FMT. Println ("Cancel the context", I) return CTX. ERR ()}} return Nil}func main () {ctx, Cancel: = context. Withtimeout (context. Background (), 4*time. Second) defer cancel () fmt. Println ("Hey, I ' m going to does some work") WG. ADD (1) Go work (CTX) WG. Wait () fmt. Println ("finished. I ' m Going Home ")}  
  5. The
  6. Cutoff time cancels the context. Withdeadline

      Package Mainimport ("context" "FMT" "Time") Func main () {d: = time. Now (). ADD (1 * time. Second) ctx, Cancel: = context. Withdeadline (context. Background (), D)//Even though ctx'll be expired, it's good practice to call its//cancelation function in any Case.    Failure to does so is keep the//context and its parent alive longer than necessary. Defer Cancel () Select {Case <-time. After (2 * time. Second): FMT. Println ("Oversleep") case <-ctx. Done (): FMT. Println (CTX. ERR ())}}  

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.