前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go Context深入学习笔记

Go Context深入学习笔记

作者头像
陌无崖
发布2019-08-16 17:42:27
7460
发布2019-08-16 17:42:27
举报

点击蓝字关注我吧

导语

在我们的程序中经常会用到Golang中协程的特性,用这个特性,我们可以轻轻松松并发上万个进程,Golang中称之为协程,而且可以充分利用我们计算机的CPU,轻轻松松解决高并发问题,但是同时也带来了问题,这么多协程,应该怎么管理?万一我想停止一个协程,该怎么办,要知道协程一旦运行,自己是不会停止的。

Context介绍

Context被称之为上下文,用它我们可以,它可以实时跟踪我们的每一个Go协程,有了这个特性,我们只需要了解怎么使用就可以了。

context接口如下:

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}
  • Deadline方法会返回设置的截止时间,如果协程运行到了这个时间点,Context则会自动触发取消,实现协程的关闭。
  • Done方法是一个只读的通道,它意味着如果可以读,则说明我们的context发起的取消。
  • Err()方法是在运营中出现错误会及时的返回
  • Value()方法则可以在跟踪协程是带上key-value的值。

小试牛刀

func main() {
  ctx, cancel := context.WithCancel(context.Background())
  go func(ctx context.Context) {
    for {
      select {
      case <-ctx.Done():
        fmt.Println("查询到有协程停止")
        return
      default:
        fmt.Println("gorouting正在有效时间内运行")
        time.Sleep(2 * time.Second)
      }
    }
  }(ctx)

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

}

上面的代码中首先我们使用了context.Backgroud()创建了一个上下文根节点,这个根节点的值时空的,我们利用WithCancel()基于根节点创建了一个可以取消的上下文,在go协程中我们使用了这个上下文来进行跟踪。我们利用select判断是否<-ctx.Done收到了取消的信号(因为这是一个通道,在为读到数据之前一直是堵塞的,因此可以保证我们的协程可以在主线程之前执行),我们可以和很轻松的看到在主线程中也就是我们的main函数在2秒后执行了cancel()函数,这是我们的协程读到数据,继续执行。最终主线程结束。看到这,有没有觉得这很像我们自定义的一个通道嘛,我们利用通道实现堵塞也可以轻易实现这个功能,不要着急,context可做的远不止这些。让我们紧接着再来看一段代码:

func main() {
  ctx, cancel := context.WithCancel(context.Background())
  go Run(ctx,"协程1正在运行")
  go Run(ctx,"协程2正在运行")
  go Run(ctx,"协程3正在运行")

  time.Sleep(10 * time.Second)
  fmt.Println("开始取消协程的运行")
  cancel()
  //为了检测监控过是否停止,如果没有监控输出,就表示停止了
  time.Sleep(5 * time.Second)
}

func Run(ctx context.Context, name string) {
  for {
    select {
    case <-ctx.Done():
      fmt.Println(name,"查询到有协程停止")
      return
    default:
      fmt.Println(name,"gorouting正在有效时间内运行")
      time.Sleep(2 * time.Second)
    }
  }
}

在这个事例中,共启动了3个协程,这3个协程,分别都用了一个Context上下文进行跟踪,此时如果cancel函数被使用,3个协程都均会关闭,所以,上下文不仅能关联一个协程,同时也能控制多个协程的关闭。

扩展

在上面的事例中我们均用到了一个函数那就是context.Background(),其实在context包中,不仅仅只提供了这一个函数来提供创建空根节点,还有一个叫做TODO的函数,这个函数和Background一样 都是一个结构体,且他们的特点都是不可取消,没有截止时间,也没有携带任何的Contxet,所以也可以被用作根节点。另外Context根如何生成可以取消的节点呢?在上面的事例中我们使用了一个叫做WithCancel()的函数,除此之外context也提供了类似的具有不同特性的子节点生成方式如下:

  • func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) 这个函数会多传一个时间参数,这个时候到达时间后就会结束context,我们也可以自己cancel
  • func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) 这个函数通上一个函数类似会多传一个时间参数,超时间后就会触动cancel
  • func WithValue(parent Context, key, val interface{}) Context 这个函数我们可以带上数据在进行上下文跟踪时。

转载请联系作者

本文笔记参考:

https://www.flysnow.org/2017/05/12/go-in-action-go-context.html

https://draveness.me/golang/concurrency/golang-context.html

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

本文分享自 golang技术杂文 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档