Go语言Context (ctx)的基础概念与用法

前言

ctx字面意思上下文,是golang中特有的一种语法,几乎每一个程序中都会通篇传递着一个ctx。而一些框架又对其进行二次封装,诸如Gin框架中的c *gin.Context。因此此次进行ctx的学习并记录。

设计原理

这是ctx的接口部分,其提供了一个接口及许多函数、结构体(如图)。

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key any) any
}

在这里插入图片描述

其通过context.Background、context.TODO、context.WithDeadline 和 context.WithValue来返回实现这个接口的结构体。

goroutine树和ctx是上下传递的,会从顶层一步步传往底层。详见

在这里插入图片描述

常见用法

控制goroutine

多个goroutine运行,如何控制其结束?
最Low的办法自然是造一个bool全局变量,多个goroutine中检测其bool值,若改变,则停止。
但这样显然是不合理的,于是便可以通过select+chan来进行控制,即在goroutine中放一个select,其中通过chan到某个值,就停止。

func main() {
	stop := make(chan bool)

	go func() {
		for {
			select {
			case <-stop:    // 收到了停滞信号
				fmt.Println("监控退出,停止了...")
				return
			default:
				fmt.Println("goroutine监控中...")
				time.Sleep(2 * time.Second)
			}
		}
	}()

	time.Sleep(10 * time.Second)
	fmt.Println("可以了,通知监控停止")
	stop<- true

	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
	time.Sleep(5 * time.Second)
}

但这样也有很大的局限性,比如我的goroutine中又新建了goroutine,这种无法在程序开始前写到的地方,该如何接收?
因此便可以用到ctx来进行控制。

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				fmt.Println("监控退出,停止了...")
				return
			default:
				fmt.Println("goroutine监控中...")
				time.Sleep(2 * time.Second)
			}
		}
	}(ctx)

	time.Sleep(10 * time.Second)
	fmt.Println("可以了,通知监控停止")
	cancel()
	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
	time.Sleep(5 * time.Second)
}

两个方法看似一样,只是将手动向<-stop收发,换成了使用cancel()<-ctx.Done()。根据源码可以看出,其底层原理也是通过chan在进行通信,但是由于ctxgoroutine树,所以可以解决goroutine中新建goroutine的问题,从而更优雅的关闭goroutine。

传递元数据

通过ctx.Value("key")来读取,通过ctx = context.WithValue(ctx, "key", value)来设置。

但仅建议传递内置类型的数据。不建议传递重要数据,比如传递 签名、trace_id这类值。

超时控制

即通过context.WithDeadline(ctx, time.Now().Add(10*time.Second))设置这个ctx的关闭时间。

通过设置deadline,可以防止一个请求超时导致服务器问题。

需要注意的是不同ctx中deadline不一致导致问题。
(一次我使用context.Context*gin.Context时遇到了莫名其妙的超时,就是因为这个不一致)

使用注意事项

1、不要把 Context 放在结构体中,要以参数的方式传递。
2、以 Context 作为参数的函数方法,应该把 Context 作为第一个参数,放在第一位。
3、给一个函数方法传递 Context 的时候,不要传递 nil,如果不知道传递什么,就使用 context.TODO。
4、Context 的 Value 相关方法应该传递必须的数据,不要什么数据都使用这个传递。
5、Context 是线程安全的,可以放心的在多个 goroutine 中传递。

参考文献

6.1 上下文 Context #

深度解密Go语言之context

一文吃透 Go 语言解密之上下文 Context

【Go语言】小白也能看懂的context包详解:从入门到精通

Golang 中 context 的使用方式

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言中的context包提供了一种在跨多个goroutine之间传递请求范围数据、取消信号和截止时间的方法。它是Go语言中处理并发和并行的重要工具之一。 Context类型是context包中的核心类型,它代表了一个请求的上下文。通过创建一个Context对象,我们可以在程序中传递请求相关的数据和信号。Context对象可以被用于控制goroutine的生命周期,以及在需要时取消或超时处理。 Context包含了一些重要的方法和属性,例如: 1. WithCancel(parent Context) (ctx Context, cancelFunc CancelFunc):创建一个可取消的Context,并返回一个取消函数。当调用取消函数时,所有从该Context派生的子Context都会被取消。 2. WithDeadline(parent Context, deadline time.Time) (ctx Context, cancelFunc CancelFunc):创建一个带有截止时间的Context。当截止时间到达时,所有从该Context派生的子Context都会被取消。 3. WithTimeout(parent Context, timeout time.Duration) (ctx Context, cancelFunc CancelFunc):创建一个带有超时时间的Context。当超过指定的时间后,所有从该Context派生的子Context都会被取消。 4. WithValue(parent Context, key interface{}, value interface{}) Context:创建一个带有键值对的Context。这个方法可以用来传递请求范围的数据。 使用context包可以有效地管理并发操作,避免资源泄漏和无限等待的情况。在编写并发代码时,合理使用context包可以提高程序的可读性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值