go筆記--幾個例子理解context的做用

go筆記--幾個例子理解context的做用


常常在http框架裏面看到一個context參數,它是作什麼的呢,先簡單看看它的定義。框架

context interface

type Context interface {
    // Deadline returns the time when work done on behalf of this context
    // should be canceled. Deadline returns ok==false when no deadline is
    // set. Successive calls to Deadline return the same results.
    Deadline() (deadline time.Time, ok bool)

    // Done returns a channel that's closed when work done on behalf of this
    // context should be canceled. Done may return nil if this context can
    // never be canceled. Successive calls to Done return the same value.
    //
    // WithCancel arranges for Done to be closed when cancel is called;
    // WithDeadline arranges for Done to be closed when the deadline
    // expires; WithTimeout arranges for Done to be closed when the timeout
    // elapses.
    //
    // Done is provided for use in select statements:
    //
    //  // Stream generates values with DoSomething and sends them to out
    //  // until DoSomething returns an error or ctx.Done is closed.
    //  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:
    //          }
    //      }
    //  }
    //
    // See https://blog.golang.org/pipelines for more examples of how to use
    // a Done channel for cancelation.
    Done() <-chan struct{}

    // Err returns a non-nil error value after Done is closed. Err returns
    // Canceled if the context was canceled or DeadlineExceeded if the
    // context's deadline passed. No other values for Err are defined.
    // After Done is closed, successive calls to Err return the same value.
    Err() error

    // Value returns the value associated with this context for key, or nil
    // if no value is associated with key. Successive calls to Value with
    // the same key returns the same result.
    //
    // Use context values only for request-scoped data that transits
    // processes and API boundaries, not for passing optional parameters to
    // functions.
    //
    // A key identifies a specific value in a Context. Functions that wish
    // to store values in Context typically allocate a key in a global
    // variable then use that key as the argument to context.WithValue and
    // Context.Value. A key can be any type that supports equality;
    // packages should define keys as an unexported type to avoid
    // collisions.
    //
    // Packages that define a Context key should provide type-safe accessors
    // for the values stored using that key:
    //
    //  // Package user defines a User type that's stored in Contexts.
    //  package user
    //
    //  import "context"
    //
    //  // User is the type of value stored in the Contexts.
    //  type User struct {...}
    //
    //  // key is an unexported type for keys defined in this package.
    //  // This prevents collisions with keys defined in other packages.
    //  type key int
    //
    //  // userKey is the key for user.User values in Contexts. It is
    //  // unexported; clients use user.NewContext and user.FromContext
    //  // instead of using this key directly.
    //  var userKey key = 0
    //
    //  // NewContext returns a new Context that carries value u.
    //  func NewContext(ctx context.Context, u *User) context.Context {
    //      return context.WithValue(ctx, userKey, u)
    //  }
    //
    //  // FromContext returns the User value stored in ctx, if any.
    //  func FromContext(ctx context.Context) (*User, bool) {
    //      u, ok := ctx.Value(userKey).(*User)
    //      return u, ok
    //  }
    Value(key interface{}) interface{}
}

能夠看到它一個接口類型、主要包含4個成員,咱們暫時不知道它們的意義。ide

Deadline() (deadline time.Time, ok bool)

    Done() <-chan struct{}

    Err() error

    Value(key interface{}) interface{}

先看一個簡單的例程

package main

import (
    "context"
    "fmt"
    "time"
)

func cancel1() {
        // gen generates integers in a separate goroutine and
    // sends them to the returned channel.
    // The callers of gen need to cancel the context once
    // they are done consuming generated integers not to leak
    // the internal goroutine started by gen.
    gen := func(ctx context.Context) <-chan int {
        dst := make(chan int)
        n := 1
        go func() {
            for {
                select {
                case <-ctx.Done():
                    fmt.Println("ctx.Done()")
                    return // returning not to leak the goroutine
                case dst <- n:
                    n++
                }
            }
        }()
        return dst
    }

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel() // cancel when we are finished consuming integers

    for n := range gen(ctx) {
        fmt.Println(n)
        if n == 5 {
            break
        }
    }
}

func main() {

    fmt.Println("-----start-----")

    fmt.Println("call cancel1()")
    go cancel1()
    fmt.Println("cancel1() end")

    time.Sleep(time.Second)
    fmt.Println("----end------")
}

控制檯會輸出
函數

咱們能夠看到,for循環執行了5次 context 的派生函數gen()後,經過cancel()函數退出了gen裏面的協程。this

那麼它在這裏的做用也就清楚了:code

context的做用

設置截止日期,超時或調用取消函數來通知全部使用任何派生 context 的函數來中止運行並返回。協程

我們這是取消函數對象

contxt相關函數

withcancel
此函數建立從傳入的父 context 派生的新 context。父 context 能夠是後臺 context 或傳遞給函數的 context。
返回派生 context 和取消函數。只有建立它的函數才能調用取消函數來取消此 context。blog

deadline接口

WithDeadline()返回其父項的派生 context,當截止日期超過或取消函數被調用時,該 context 將被取消。例如,您能夠建立一個將在之後的某個時間自動取消的 context,並在子函數中傳遞它。當由於截止日期耗盡而取消該 context 時,獲此 context 的全部函數都會收到通知去中止運行並返回。
小例:

func deadline1() {
    d := time.Now().Add(1200 * time.Millisecond)
    ctx, cancel := context.WithDeadline(context.Background(), d)

    // Even though ctx will be expired, it is good practice to call its
    // cancelation function in any case. Failure to do so may keep the
    // context and its parent alive longer than necessary.
    defer cancel()

    select {
    case <-time.After(1 * time.Second):
        fmt.Println("overslept")
    case <-ctx.Done():
        fmt.Println(ctx.Err())
    }

}

func main() {

    fmt.Println("-----start-----")

    fmt.Println("call deadline1()")
    go deadline1()
    fmt.Println("deadline1() end")

    time.Sleep(3*time.Second)
    fmt.Println("----end------")
}

withtimeout()和WithDeadline()相似,不一樣之處在於它將持續時間做爲參數輸入而不是時間對象。

timeout

// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()

select {
case <-time.After(1 * time.Second):
    fmt.Println("overslept")
case <-ctx.Done():
    fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}

Output:
context deadline exceeded

如今再看看這context的interface,就能簡單明瞭它們的意義了。

// 返回超時期限。
    Deadline() (deadline time.Time, ok bool)

    // 輔助cancel
    Done() <-chan struct{}

    // 獲取返回的錯誤
    Err() error

    Value(key interface{}) interface{}

但是還有個Value, 那咱們再看看。

還有一個withValue() 函數 、
此函數接收 context 並返回派生 context,其中值 val 與 key 關聯,並經過 context 樹與 context 一塊兒傳遞。這意味着一旦得到帶有值的 context,從中派生的任何 context 都會得到此值。不建議使用 context 值傳遞關鍵參數,而是函數應接收簽名中的那些值,使其顯式化。

Code: 
type favContextKey string

f := func(ctx context.Context, k favContextKey) {
    if v := ctx.Value(k); v != nil {
        fmt.Println("found value:", v)
        return
    }
    fmt.Println("key not found:", k)
}

k := favContextKey("language")
ctx := context.WithValue(context.Background(), k, "Go")

f(ctx, k)
f(ctx, favContextKey("color"))

Output:
found value: Go
key not found: color

那麼明顯了 Value(key interface{}) interface{} 返回的便是context關聯的鍵值。

相關文章
相關標籤/搜索