golang开发 深入理解 context

context的历史

context包在Go 1.7版本正式加入Go标准库。在加入之前我们看看Go团队核心成员Sameer Ajmani在2014年发表的一篇关于context介绍博客,地址:https://go.dev/blog/context 下面是介绍的翻译。

在Go服务器中,每个传入的请求都在自己的goroutine中处理。请求处理程序经常启动额外的goroutine来访问后端,如数据库和RPC服务。处理请求的goroutine集合通常需要访问特定于请求的值,例如最终用户的身份、授权令牌和请求的截止日期。当一个请求被取消或超时时,所有处理该请求的goroutine都应该迅速退出,这样系统就可以回收它们正在使用的任何资源。

在Google,我们开发了一个 context 包,可以轻松地将请求范围内的值、取消信号和截止日期跨API边界传递给处理请求所涉及的所有goroutine。该软件包作为上下文公开提供。

可以看到介绍里面主要说的是,应用中怎么控制大量的goroutine退出释放资源、请求范围内怎么传值,也就是说,context的引入主要是为了解决这两个问题。

context的基本使用

context初始化的方法有五个,一个是main goroutine 初始化,一个主要是是用来传值的,经常使用的就三个。

未超时的时候,执行取消,依赖取消函数

复制代码
func main() {
    ctx := context.Background()
    ctx = context.WithValue(ctx, "main", "main-value")

    ctx1,cancel := context.WithDeadline(ctx, time.Now().Add(3*time.Second))
    go cancelFunc(ctx1)
    //取消函数
    cancel()
    time.Sleep(time.Duration(5*time.Second))
}

执行结果
main-param main-value
退出协程

超时的时候执行结果,超过三秒的超时时间,依赖超时时间

复制代码
func main() {
    ctx := context.Background()
    ctx = context.WithValue(ctx, "main", "main-value")

    ctx1,_ := context.WithDeadline(ctx, time.Now().Add(3*time.Second))
    go cancelFunc(ctx1)
    time.Sleep(time.Duration(5*time.Second))
}

执行结果
main-param main-value
1秒
1秒
1秒
退出协程

看注释和执行流程,基本能理解,main函数调用cancel就能取消正在执行的goroutine协程。WithDeadline和WithTimeout 除过可以通过调用取消函数,还可以通过设置超时时间点和时间段取消goroutine的执行。

context如何控制多个goroutine,如何传值给多个goroutine

看图示,root context控制着所有的goroutine,context1控制着 goroutine2 goroutine3 goroutine4 goroutine5 goroutine6 goroutine7,context2 控制着goroutine4 goroutine5,context3 控制着goroutine6 goroutine7。

使用图示的goroutine 编写测试代码

复制代码
package main

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

func func1(ctx context.Context) {
    fmt.Println("func1-main-param", ctx.Value("main"))
    ctx1 := context.WithValue(ctx, "func1-param", "func1-value")
    go func2(ctx1)
    select {
    case <-ctx1.Done():
        fmt.Println("goroutine 1 exit")
    }
}

func func2(ctx context.Context) {
    fmt.Println("func2-main-param", ctx.Value("main"))
    fmt.Println("func2-func1-param", ctx.Value("func1-param"))
    ctx2, _ := context.WithCancel(ctx)
    go func4(ctx2)
    go func5(ctx2)
    select {
    case <-ctx2.Done():
        fmt.Println("goroutine 2 exit")
    }
}

func func4(ctx context.Context) {
    select {
    case <-ctx.Done():
        fmt.Println("goroutine 4 exit")
    }
}

func func5(ctx context.Context) {
    select {
    case <-ctx.Done():
        fmt.Println("goroutine 5 exit")
    }
}

func main() {
    ctx := context.Background()
    ctx = context.WithValue(ctx, "main", "main-value")
    ctx,cancel := context.WithCancel(ctx)
    go func1(ctx)
    cancel()
    time.Sleep(time.Duration(5*time.Second))
}

运行结果

复制代码
func1-main-param main-value
goroutine 1 exit
func2-main-param main-value
func2-func1-param func1-value
goroutine 2 exit
goroutine 5 exit
goroutine 4 exit

可以看到最终实现结果和场景,跟Sameer Ajmani在博客描述的是一样的,context的出现主要两个作用,控制 goroutine,goroutine之间传值。

相关推荐
Wenweno0o9 小时前
Eino - 错误处理与稳定性
golang·智能体·eino
王码码203510 小时前
Go语言中的Elasticsearch操作:olivere实战
后端·golang·go·接口
Tomhex10 小时前
Go语言import用法详解
golang·go
Tomhex12 小时前
Golang空白导入的真正用途
golang·go
Wenweno0o14 小时前
Eino - 从0到1跑通大模型调用
golang·大模型·智能体·eino
不会写DN15 小时前
IPv4 与 IPv6 的核心区别
计算机网络·面试·golang
Flandern111116 小时前
Go程序员学习AI大模型项目实战02:给 AI 装上“大脑”:从配置解包到流式生成的深度拆解
人工智能·后端·python·学习·golang
ん贤20 小时前
Go GC 非玄学,而是 CPU 和内存的权衡
开发语言·后端·golang·性能调优·gc
Dontla1 天前
go语言Windows安装教程(安装go安装Golang安装)(GOPATH、Go Modules)
开发语言·windows·golang
铁东博客1 天前
Go实现周易大衍筮法三变取爻
开发语言·后端·golang