站长资讯网
最全最丰富的资讯网站

Go 语言中的 Context 详解

Go 语言中的 Context 详解

1. 什么是 Context?

在 Go 1.7 版本之前,context 还是非编制的,它存在于 golang.org/x/net/context 包中。

后来,Golang 团队发现 context 还挺好用的,就把 context 收编了,在 Go 1.7 版本正式纳入了标准库。

Context,也叫上下文,它的接口定义如下

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

可以看到 Context 接口共有 4 个方法

  • Deadline:返回的第一个值是 截止时间,到了这个时间点,Context 会自动触发 Cancel 动作。返回的第二个值是 一个布尔值,true 表示设置了截止时间,false 表示没有设置截止时间,如果没有设置截止时间,就要手动调用 cancel 函数取消 Context。
  • Done:返回一个只读的通道(只有在被cancel后才会返回),类型为 struct{}。当这个通道可读时,意味着parent context已经发起了取消请求,根据这个信号,开发者就可以做一些清理动作,退出goroutine。
  • Err:返回 context 被 cancel 的原因。
  • Value:返回被绑定到 Context 的值,是一个键值对,所以要通过一个Key才可以获取对应的值,这个值一般是线程安全的。

2. 为何需要 Context?

当一个协程(goroutine)开启后,我们是无法强制关闭它的。

常见的关闭协程的原因有如下几种:

  1. goroutine 自己跑完结束退出
  2. 主进程crash退出,goroutine 被迫退出
  3. 通过通道发送信号,引导协程的关闭。

第一种,属于正常关闭,不在今天讨论范围之内。

第二种,属于异常关闭,应当优化代码。

第三种,才是开发者可以手动控制协程的方法,代码示例如下:

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)  }

例子中我们定义一个stop的chan,通知他结束后台goroutine。实现也非常简单,在后台goroutine中,使用select判断stop是否可以接收到值,如果可以接收到,就表示可以退出停止了;如果没有接收到,就会执行default里的监控逻辑,继续监控,只到收到stop的通知。

以上是一个 goroutine 的场景,如果是多个 goroutine ,每个goroutine 底下又开启了多个 goroutine 的场景呢?在 飞雪无情的博客 里关于为何要使用 Context,他是这么说的

chan+select的方式,是比较优雅的结束一个goroutine的方式,不过这种方式也有局限性,如果有很多goroutine都需要控制结束怎么办呢?如果这些goroutine又衍生了其他

赞(0)
分享到: 更多 (0)
网站地图   沪ICP备18035694号-2    沪公网安备31011702889846号