前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >直击底层:原来 Context 还能这么用?(下)

直击底层:原来 Context 还能这么用?(下)

作者头像
小锟哥哥
发布2022-05-10 08:25:25
2550
发布2022-05-10 08:25:25
举报
文章被收录于专栏:GoLang全栈

上一篇文章我们提到:

context 主要作用:

  • key-value值
  • 取消信号
  • 超时信号

取消信号

首先说下场景:

我们有时会遇到,在处理某些任务时,在外部可以对其及时终止,比如传输文件时。

所以我们的 Context 也提供了一个传递取消信号的方法,我们只需要调用这个方法,就会像监听在这个上下文的 Done 通道上的方法。

注意:这里不是直接干掉这个方法,仅仅只是传递一个信号。

范例代码如下:

代码语言:javascript
复制
func req3(ctx context.Context)  {
 // 监听取消信息
 select {
  case <-ctx.Done():
   fmt.Println("收到取消信号了")
   return
 }
}

func main() {
 ctx,cancel := context.WithCancel(context.Background())
 //  启动一个协程
 go req3(ctx)
 
 // 模拟耗时操作
 num := 1
 for {
  if num>3 {
   cancel()
  }
  time.Sleep(time.Second)
  num++
 }
}

你会发现和 WithValue 相比,返回参数里面他多了一个 cancel 方法。

这个方法一但调用就会向 Context 里面传送取消信号。

再次强调,是传递取消信号,不是直接干掉那个方法。

至于我们收到取消信号后,做什么完全取决你的业务需要。

超时信号

这个很好理解,就是超时后他们自动为我们调用 cancel 方法。

这样我们就免去了自己去写定时到了时间调取消。

范例代码如下:

代码语言:javascript
复制
func req4(ctx context.Context)  {
 select {
 case <-ctx.Done():
  fmt.Println("超时或者被取消")
  return
 }
}

func main() {

 ctx,cancel := context.WithTimeout(context.Background(),time.Second*3)
 go req4(ctx)

 // 模拟耗时操作
 num := 1
 for {
  if num>5 {
   cancel()
  }
  time.Sleep(time.Second)
  num++
 }
}

看完代码你才猜会输出几次“超时或者被取消”?

答案是只有一次。

虽然我们在里面有两处地方调用,但是他只会打印一次,因为在传递了一次取消之后这个通道就关闭了。

如果你实在是需要知道到底是超时还是手动取消调用的,你可以收到信号时打印下 ctx.Error() 方法所输出的值。

比如这样:

代码语言:javascript
复制
func req4(ctx context.Context)  {
 select {
 case <-ctx.Done():
  fmt.Println("超时或者被取消",ctx.Err().Error())
  return
 }
}

当时手动取消的,会打印:

代码语言:javascript
复制
超时或者被取消 context deadline exceeded

如果是超时取消的,会打印:

代码语言:javascript
复制
超时或者被取消 context canceled

总结

在看完这几个用处之后,是不是对 Context 的作用有了更深的了解了。

因为我们在开发中,并发的情况会经常出现,于是会开很多的 goroutine 在这么多的 goroutine 之间传递信号这些一般都会考虑使用 Context 来完成。

所以他的重要性还是很大的。

最后我们来看下官方给我们的使用原则:

使用 Context 的程序包需要遵循如下的原则来满足接口的一致性以及便于静态分析。

  • 不要把 Context 存在一个结构体当中,显式地传入函数。Context变量需要作为第一个参数使用,一般命名为ctx;
  • 即使方法允许,也不要传入一个nil的Context,如果你不确定你要用什么 Context 的时候传一个context.TODO;
  • 使用 context 的 Value 相关方法只应该用于在程序和接口中传递的和请求相关的元数据,不要用它来传递一些可选的参数;
  • 同样的Context可以用来传递到不同的 goroutine 中,Context在多个 goroutine 中是安全的;

在子Context被传递到的goroutine中,应该对该子Context的Done信道(channel)进行监控,一旦该信道被关闭(即上层运行环境撤销了本 goroutine 的执行),应主动终止对当前请求信息的处理,释放资源并返回。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GoLang全栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • context 主要作用:
  • 取消信号
  • 超时信号
  • 总结
    • 最后我们来看下官方给我们的使用原则:
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档