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之间传值。

相关推荐
tyler_download3 小时前
golang 实现比特币内核:处理椭圆曲线中的天文数字
golang·blockchain·bitcoin
疯狂的程需猿4 小时前
一个百度、必应搜索引擎图片获取下载的工具包
golang·图搜索
明月看潮生6 小时前
青少年编程与数学 02-003 Go语言网络编程 09课题、Cookie
青少年编程·golang·网络编程·编程与数学
明月看潮生6 小时前
青少年编程与数学 02-003 Go语言网络编程 15课题、Go语言URL编程
开发语言·网络·青少年编程·golang·编程与数学
明月看潮生6 小时前
青少年编程与数学 02-003 Go语言网络编程 14课题、Go语言Udp编程
青少年编程·golang·网络编程·编程与数学
hlsd#8 小时前
go 集成go-redis 缓存操作
redis·缓存·golang
qq_1728055921 小时前
GIN 反向代理功能
后端·golang·go
__AtYou__1 天前
Golang | Leetcode Golang题解之第535题TinyURL的加密与解密
leetcode·golang·题解
kevin_tech1 天前
Go API 多种响应的规范化处理和简化策略
开发语言·后端·golang·状态模式
幺零九零零1 天前
【Golang】sql.Null* 类型使用(处理空值和零值)
数据库·sql·golang