Go Context

context上下文的意思, 可以用来做超时管理 ,context 可以设置截止日期,超时或调用取消函数来通知所有使用任何派生 context 的函数来停止运行并返回。
1.创建 context
(1) ctx := context.Background()
返回一个空 context。这只能用于高等级(在 main 或顶级请求处理中)用于派生。
(2) ctx := context.TODO() Context
返回也是空context,与Bg的区别在于静态分析工具可以使用它来验证 context 是否正确传递,这是一个重要的细节,因为静态分析工具可以帮助在早期发现潜在的错误,并且可以连接到 CI/CD 管道。

var (
    background = new(emptyCtx)
    todo = new(emptyCtx)
)

2.使用context
(1) context.WithValue(parent Context, key, val interface{}) (ctx Context, cancel CancelFunc)
此函数接收 context 并返回派生 context,其中值 val 与 key 关联,并通过 context 树与 context 一起传递。这意味着一旦获得带有值的 context,从中派生的任何 context 都会获得此值。不建议使用 context 值传递关键参数,而是函数应接收签名中的那些值,使其显式化。

ctx := context.WithValue(context.Background(), key, "test")

(2) context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)
返回派生 context 和取消函数。只有创建它的函数才能调用取消函数来取消此 context。如果您愿意,可以传递取消函数,但是,永远不要传递取消函数。

ctx, cancel := context.WithCancel(context.Background())

(3) context.WithDeadline(parent Context, d time.Time) (ctx Context, cancel CancelFunc)
此函数返回其父项的派生 context,当截止日期超过或取消函数被调用时,该 context 将被取消。例如,您可以创建一个将在以后的某个时间自动取消的 context,并在子函数中传递它。当因为截止日期耗尽而取消该 context 时,获此 context 的所有函数都会收到通知去停止运行并返回。

ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2 * time.Second))

(4) context.WithTimeout(parent Context, timeout time.Duration) (ctx Context, cancel CancelFunc)
此函数类似于 context.WithDeadline。不同之处在于它将持续时间作为参数输入而不是时间对象。此函数返回派生 context,如果调用取消函数或超出超时持续时间,则会取消该派生 context。

ctx, cancel := context.WithTimeout(context.Background(), 2 * time.Second)

Demo:
一个超时的例子:

package main

import (
  "context"
  "fmt"
  "math/rand"
  "time"
)

/* Context 可以用来做超时管理
*
*/

func sleepRandom(fromFunction string, ch chan int){
  defer func(){fmt.Println(fromFunction, "sleepRandom complete")}()

  seed := time.Now().UnixNano()
  r := rand.New(rand.NewSource(seed))
  randomNumber := r.Intn(100)
  sleeptime := randomNumber + 100
  fmt.Println(fromFunction, "starting sleep for", sleeptime, "ms")
  time.Sleep(time.Duration(sleeptime) * time.Millisecond)
  fmt.Println(fromFunction, "Waking up, slept for", sleeptime, "ms")
  if ch != nil{
      ch <- sleeptime
  }
}

func sleepRandomContext(ctx context.Context, ch chan bool){
  defer func(){
      fmt.Println("sleepRandomContext complete")
      ch <- true
  }()
  sleeptimeChan := make(chan int)
  go sleepRandom("sleepRandomContext", sleeptimeChan)

  select{
  case <-ctx.Done():
      fmt.Println("sleepRandomContext: Time to return")
  case sleeptime := <- sleeptimeChan:
      fmt.Println("sleep for ", sleeptime, "ms")
  }
}

func doWorkContext(ctx context.Context){
  ctxWithTimeout, cancelFunction := context.WithTimeout(ctx, time.Duration(100) * time.Millisecond)

  fmt.Println("begin")
  time.Sleep(1 * time.Second)
  defer func(){
      fmt.Println("doWorkContext complete")
      cancelFunction()
  }()

  ch := make(chan bool)
  go sleepRandomContext(ctxWithTimeout, ch)

  select {
  case <- ctx.Done():
      fmt.Println("doWorkContext: Time to return")
  case <- ch:
      fmt.Println("sleepRandomContext returns")
  }

}

func main(){
  ctx := context.Background()

  /*
   * Context 可以用来做超时管理,当context关闭的时候,函数也跟着关闭了
   */

  //cancelFunction 取消函数
  //可以直接取消当前的context,context -> nil
  ctxWithCancel, cancelFunction := context.WithCancel(ctx)

  defer func(){
      fmt.Println("Main Defer: canceling context")
      cancelFunction()
  }()

  go func(){
      time.Sleep(1 * time.Second)
      //sleepRandom("Main", nil)
      cancelFunction()
      fmt.Println("Main Sleep complete. canceling context")
  }()
  doWorkContext(ctxWithCancel)
}

你可能感兴趣的:(Go Context)