golang标准库 —— context包

一、背景描述

Golang中我们可以使用sync.WaitGroup进行并发控制。如下,

wg.Wait()会等待2个goroutine完成后继续执行。

在实际的业务中,我们可能会有这么一种场景:需要我们主动的通知某一个goroutine结束。比如我们开启一个后台goroutine一直做事情,比如监控,如果主程序,就需要通知这个监控goroutine结束,不然它会一直跑,就泄漏了。

chan通知

golang一直推崇着不要通过共享内存来通信,而应该通过通信来共享内存,我们可以使用select + chan的方式来实现。

当主程序完成后,向stop chan中推送消息,goroutine接收到消息后推出监控。

context

Context包是Google内部专门用来简化 对于处理单个请求的多个 goroutine 之间与请求域的数据、取消信号、截止时间等相关操作,这些操作可能涉及多个 API 调用。在Golang1.7版本之后开源出来了。

1.6及之前版本可以参考golang.org/x/net/context

我们可以使用context包的功能里替换select + chan的模式。但是其本质还是select + chan。如下,

  • context.Background() 获取一个空的context
  • ctx, cancel := context.WithCancel(context.Background())扩展一个带有取消功能的coontext,同时得到的cancel是以后取消的方法。当需要执行取消操作的时候,cancel()执行一下就可以了。
  • ctx.Done()获取一个chan。当chan里面可以获取到东西的时候,就表示该context已经被取消了。

context也是线程安全的,所以也支持传入多个goroutine

二、源码剖析

核心接口

emptyCtx

Context接口并不需要我们自己来实现,因为context包内已经帮我们实现了一个emptyCtx

emptyCtx是一个不可取消,没有设置截止时间,没有携带任何值的Context

background & todo

context提供了2个方法来获取emptyCtxbackgroudtodo本质上没有区别,一般通过要干什么事情来区分调用哪一个。

  • background,主要用于main函数、初始化以及测试代码中
  • todo,当你不清楚需要使用那个时,就用它

WithCancel、 WithDeadline、 WithTimeout、 WithValue

当有了emptyCtx以后,我们可以使用golang为我们提供的4个扩展方法,对emptyCtx进行扩展。如下,

1、WithCancel

WithCancel函数,传递一个父Context作为参数,返回cancelCtx,以及一个CancelFunc用来取消Context

使用方法正如上面的监控案例。

2、WithDeadline、WithTimeout

WithDeadline它会多传递一个截止时间参数,意味着到了这个时间点,会自动取消Context,当然我们也可以不等到这个时候,可以提前通过取消函数进行取消。

WithTimeout底层调用的WithDeadline 区别就是一个传入的具体是简单,一个传入的是偶多少时间之后。

上面案例,当时间到了之后,就自动停止了。当我么你再去调用cancel方法时,会自动忽略。

3、WithValue

通过Context我们也可以传递一些必须的元数据,这些数据会附加在Context上以供使用。

取值时,key必须对应上,否则返回nil