美文网首页
go语言的context怎么用

go语言的context怎么用

作者: GUIN蚂蚁 | 来源:发表于2022-07-24 20:51 被阅读0次

    官方文档

    背景

    context直译就是上下文的意思,这样翻译自然是不知道它是用来做什么的。我看先来看看它的官方文档:


    context

    go服务的每个请求都是使用goroutine来处理,有时候还会产生额外的goroutine。当这些goroutine处理完请求后,需要立即退出,这样才能释放goroutine的资源。
    所以官方开发了context包来帮助我们管理/释放goroutine资源。

    说明

    这是 Context 定义

    // Context 携带截止日期、取消信号和请求范围的值 跨越 API 边界。
    // 多个 goroutine同时使用它的方法是安全的。
    type Context interface {
        // Done 返回一个在取消此上下文时关闭的通道或超时。
        Done() <-chan struct{}
    
        // Err 指示为什么在 Done 通道关闭后取消此上下文。
        Err() error
    
        // Deadline 返回取消此 Context 的时间(如果有)。
        Deadline() (deadline time.Time, ok bool)
    
        // Value 返回与 key 关联的值,如果没有则返回 nil。
        Value(key interface{}) interface{}
    }
    

    使用方法

    WithCancel

    定义一个函数,在goroutine时调用,可以写业务代码,异步处理业务数据

    func do(ctx context.Context) {
        // 一直循环,知道 ctx.Done() 收到信号
        for {
            select {
            case v := <-ctx.Done():
                // cancel 被调用时,会接收到channel信号,然后退出
                fmt.Println("goroutine exit", v)
                return
            default:
                // 延时一下
                time.Sleep(time.Millisecond * 500)
                fmt.Println(time.Now())
            }
        }
    }
    

    context.WithCancel会返回一个新的contextcancel,将ctx传递给goroutine去监听,如果cancel被调用,Done()就会收到信号,然后退出goroutine。
    context.Background()是一个空的context,它作为根context是必须的。这里要注意的是context.WithCancelcancel需要手动调用。

    func main() {
        parentCtx := context.Background()
    
        // 创建一个 cancel context
        ctx, cancel := context.WithCancel(parentCtx)
        defer cancel()
    
        go func() {
            do(ctx)
        }()
    
        //  等一会,调用cancel() 释放 goroutine
        time.Sleep(time.Second * 5)
        cancel()
        time.Sleep(time.Second * 1)
    }
    

    WithTimeout

    context.WithTimeout 可以指定超时时间,并在超时后自动调用cancel(),这样可以可以向do()函数里面的ctx.Done()发送结束消息,然后return。这样使用方法,可以防止goroutine被阻塞,而导致内存泄露或阻塞了外部的业务(wg.Wait()阻塞)。

    func main() {
        parentCtx := context.Background()
    
        // 创建一个 WithTimeout context,超时后会自动调动cancel()
        ctx, cancel := context.WithTimeout(parentCtx, time.Second*5)
        defer cancel()
    
        var wg sync.WaitGroup
        wg.Add(1)
        go func() {
            defer wg.Done()
            do(ctx)
        }()
    
        // WithTimeout 超时后,会自动调动cancel() 退出goroutine
        wg.Wait()
    }
    

    WithDeadline

    context.WithTimeout相似,context.WithDeadline可以指定在某一个具体的时间点调用cancel函数

    func main() {
        parentCtx := context.Background()
    
        after := time.Now().Add(time.Second * 5)
        // 创建一个 WithDeadline context, 指定时间调用cancel
        ctx, cancel := context.WithDeadline(parentCtx, after)
        defer cancel()
    
        var wg sync.WaitGroup
        wg.Add(1)
        go func() {
            defer wg.Done()
            do(ctx)
        }()
    
        // 等待goroutine退出
        wg.Wait()
    }
    

    WithValue

    context.WithValue与上述的三个函数不同,该函数返回值只有context,是没有cancel的。但它能存储一个kv值,提供给用户传递上下文参数信息。注意:这里的kv不是让用户存储业务数据。

    func main() {
        parentCtx := context.Background()
    
        key := "contextKey"
        var s any = "1"
        // 创建一个 WithDeadline context, 指定时间调用cancel
        valueCtx := context.WithValue(parentCtx, key, s)
    
    
        fmt.Println(ctx.Value(key), valueCtx.Value(key))
    }
    
    `context.WithValue`只会返回`context`,不会返回`cancel`,但它可以结合`WithTimeout`、`WithDeadline`、`WithCancel`使用。
    ``` go
    func main() {
        parentCtx := context.Background()
    
        key := "contextKey"
        var s any = "1"
        // 创建一个 WithDeadline context, 指定时间调用cancel
        valueCtx := context.WithValue(parentCtx, key, s)
    
        ctx, cancel := context.WithTimeout(valueCtx, time.Second*5)
        defer cancel()
    
        var wg sync.WaitGroup
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go func() {
                defer wg.Done()
                do(ctx)
            }()
        }
    
        fmt.Println(ctx.Value(key), valueCtx.Value(key))
        // 等待goroutine退出
        wg.Wait()
    }
    

    应用场景

    1. 控制或管理goroutine的释放,多goroutine管理;
    2. 传递请求的上下文的参数(不是指业务参数),例如,做链路追踪日志时,可以存放traceId.
    

    相关文章

      网友评论

          本文标题:go语言的context怎么用

          本文链接:https://www.haomeiwen.com/subject/aeknirtx.html