14

context

 3 years ago
source link: https://studygolang.com/articles/28798
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

context:

主要的用处如果用一句话来说,是在于简化在多个go routine传递上下文数据,控制goroutine的生命周期。

应用场景:

1.官方http包使用context传递请求的上下文数据

2.gRpc使用context来终止某个请求产生的routine树

3.关联任务的取消

.......

A Context carries a deadline, a cancellation signal, and other values across

基本操作:

import (
    "context"
    )
    

根Context:通过context.Background() 创建

子Context:context.WithCancel(parentContext) 创建

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

ctx可以传入到接下来的子任务,cancel可以取消当前Context,

当前Context被取消,基于他的子context都会被取消

接受取消通知  <- ctx.Done()

context.Background(),创建一个 根Context实例 =》 emptyCtx

func Background() Context {
    return background
}

background = new(emptyCtx)
type Context interface {

//返回一个time.Time,表示当前Context应该结束的时间,ok则表示有结束时间
    Deadline() (deadline time.Time, ok bool)

//返回一个关闭的channel (Done returns a channel that's closed )当timeout或者调用cancel方法时,将会触发Done
    Done() <-chan struct{}

//context被取消的原因
    Err() error


 //context实现共享数据存储的地方,是协程安全的
    Value(key interface{}) interface{}
}

context库中,有4个关键方法:

  • WithCancel 返回一个cancel函数,调用这个函数则可以主动停止goroutine。
func main() {
    root := context.Background()
    //ctx1, cancel := context.WithDeadline(root, time.Now().Add(6 * time.Second))
    ctx1, cancel := context.WithCancel(root)
    ctx2 := context.WithValue(ctx1, "key2", "value2")
    go watch(ctx2)
    time.Sleep(10 * time.Second)
    fmt.Println("通知监控停止")
    cancel()
    time.Sleep(5 * time.Second)
}

func watch(ctx context.Context) {
    for {
        select {
        case <- ctx.Done():
            fmt.Println(ctx.Value("key2"), "监控退出了。")
            return
        default:
            fmt.Println(ctx.Value("key2"), "go rountine 监控中。。。")
            time.Sleep(2 * time.Second)
        }
    }
}

输出:

value2 go rountine 监控中。。。
value2 go rountine 监控中。。。
value2 go rountine 监控中。。。
value2 go rountine 监控中。。。
value2 go rountine 监控中。。。
通知监控停止
value2 监控退出了。
  • WithValue WithValue可以设置一个key/value的键值对,可以在下游任何一个嵌套的context中通过key获取value。
func main() {
    root := context.Background()
    ctx1 := context.WithValue(root, "key1", "value1")
    ctx2 := context.WithValue(ctx1, "key2", "value2")
    fmt.Println(ctx1.Value("key1"))
    fmt.Println(ctx1.Value("key11"))
    fmt.Println(ctx2.Value("key2"))
}

输出:
value1  
<nil>  
value2
  • WithTimeout 函数可以设置一个time.Duration,到了这个时间则会cancel这个context。
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
    return WithDeadline(parent, time.Now().Add(timeout))
}


shutDown := func(interface{}) {
        ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
        defer cancel()
        if e := httpSrv.Shutdown(ctx); e != nil {
            log.Errorf("cronsun node pprof, metrics http server shutdown failed, error: %s", e.Error())
        } else {
            log.Errorf("cronsun node pprof, metrics http server shutdown success~~")
        }
    }
  • WithDeadline WithDeadline函数跟WithTimeout很相近,只是WithDeadline设置的是一个时间点。
func main() {
    d := time.Now().Add(500 * time.Millisecond) //500 ms
    ctx, cancel := context.WithDeadline(context.Background(), d)

    defer cancel()
    c := make(chan bool)
    //模拟耗时代码
    go func() {
        time.Sleep(400 * time.Millisecond) //修改数值大于 500
        c <- true                          //返回
    }()
    select {
    case flag := <-c: //从chan 获取值
        if flag {
            fmt.Println("执行任务成功")
        } else {
            fmt.Println("执行任务失败")
        }
    case <-ctx.Done(): //是ctx的通道先返回的数据
        fmt.Println("执行任务超时~")
    }
}

欢迎关注我们的微信公众号,每天学习Go知识

FveQFjN.jpg!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK