Go再学习_3.Go Context

系统管理员 2023-03-06 03:23 156阅读 0赞

前言

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

Context

  1. type Context interface {
  2. Deadline() (deadline time.Time, ok bool) // 截止时间
  3. Done() <-chan struct{
  4. } // cancel后返回,可读
  5. Err() error // context被cancel的原因
  6. Value(key interface{
  7. }) interface{
  8. } // 绑定到Context的值
  9. }

当一个协程(goroutine)开启后,我们是无法强制关闭它的。常见的关闭协程的原因有如下几种:

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

第一种,属于正常关闭,不在今天讨论范围之内。
第二种,属于异常关闭,应当优化代码。
第三种,才是开发者可以手动控制协程的方法

  1. func main() {
  2. stop := make(chan bool)
  3. go func() {
  4. for {
  5. select {
  6. case <-stop:
  7. fmt.Println("监控退出,停止了...")
  8. return
  9. default:
  10. fmt.Println("goroutine监控中...")
  11. time.Sleep(2 * time.Second)
  12. }
  13. }
  14. }()
  15. time.Sleep(10 * time.Second)
  16. fmt.Println("可以了,通知监控停止")
  17. stop<- true
  18. //为了检测监控过是否停止,如果没有监控输出,就表示停止了
  19. time.Sleep(5 * time.Second)
  20. }

第三种可以使用,但是不够优雅,好用。在goroutine比较多情况下,难以维护。所以,出现了context:

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "time"
  6. )
  7. func monitor(ctx context.Context, number int) {
  8. for {
  9. select {
  10. // 其实可以写成 case <- ctx.Done()
  11. // 这里仅是为了让你看到 Done 返回的内容
  12. case v :=<- ctx.Done():
  13. fmt.Printf("监控器%v,接收到通道值为:%v,监控结束。\n", number,v)
  14. return
  15. default:
  16. fmt.Printf("监控器%v,正在监控中...\n", number)
  17. time.Sleep(2 * time.Second)
  18. }
  19. }
  20. }
  21. func main() {
  22. ctx, cancel := context.WithCancel(context.Background())
  23. for i :=1 ; i <= 5; i++ {
  24. go monitor(ctx, i)
  25. }
  26. time.Sleep( 1 * time.Second)
  27. // 关闭所有 goroutine
  28. cancel()
  29. // 等待5s,若此时屏幕没有输出 <正在监控中> 就说明所有的goroutine都已经关闭
  30. time.Sleep( 5 * time.Second)
  31. fmt.Println("主程序退出!!")
  32. }

同时,它还有几种常用继承衍生:

  1. func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
  2. func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
  3. func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
  4. func WithValue(parent Context, key, val interface{
  5. }) Context

Context 使用注意事项:

  • 通常 Context 都是做为函数的第一个参数进行传递(规范性做法),并且变量名建议统一叫 ctx
  • Context 是线程安全的,可以放心地在多个 goroutine 中使用。
  • 当你把 Context 传递给多个 goroutine 使用时,只要执行一次 cancel 操作,所有的 goroutine 就可以收到 取消的信号
  • 不要把原本可以由函数参数来传递的变量,交给 Context 的 Value 来传递。
  • 当一个函数需要接收一个 Context 时,但是此时你还不知道要传递什么 Context 时,可以先用 context.TODO 来代替,而不要选择传递一个 nil。
  • 当一个 Context 被 cancel 时,继承自该 Context 的所有 子 Context 都会被 cancel。

发表评论

表情:
评论列表 (有 0 条评论,156人围观)

还没有评论,来说两句吧...

相关阅读

    相关 go - context 用法

    1,context作用 1,通过context,我们可以方便地对同一个请求所产生地goroutine进行约束管理,可以设定超时、deadline,甚至是取消这个请求相关的