Go Context 模块

129 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

前言

在写 Golang 项目时发现 context 模块几乎是无处不在,但在使用和理解的时候还是对其的理解总是模糊不清。context 在维护程序性能起到至关重要的作用。

作用

一个顾客进入饭店并进行点单。菜单确认后就会由服务员派单到后厨的厨师进行制作餐食。但当客户突然不想吃决定离开,这该怎么办?毫无疑问,服务员必须阻止厨师制作,防止食材浪费。

Context 模块作用就跟服务员一样,它是传递给你的函数或 Goroutines 的参数,并可以在不需要的时候立即停止它们。

是什么

context 常用的场景就是客户端终止与服务端的连接。但是当服务端正在处理一些比较重要的任务时该怎么办?

context 模块允许这些进程在不再需要时立即停止。

context 模块的使用主要有三个部分:

  • 监听取消事件
  • 发出取消事件
  • 传递请求范围数据

看下 Context 接口的源代码:

type Context interface {
    Done() <- chan struct{}
    Err() error

    Deadline() (deadline time.Time, ok bool)
    Value(key interface{}) interface{}
}

context 类型是实现了四个方法的的接口:

  • Done 方法会在取消 context 时返回一个空struct 结构的channel 。
  • Err 方法在取消时返回非零错误,否则返回零值。
  • Deadline 方法返回当任务完成时 context 应该被取消的时间。 未设置截止日期时,截止日期返回 ok==false。
  • Value 方法返回与 key 关联的 Context的值,如果没有返回 nil。使用相同 key 连续调用时 Value 方法返回的是同样的结果。

功能

监听取消事件

Done 方法和 Err 方法在会在context 取消时触发。

func handler(r *http.Request){
    ctx := r.Context()

    select {
        case <-time.After(2*time.second):
          fmt.Println("request processed")
        case <- ctx.Done():
          fmt.Println("request Cancelled")
          return
    }
}

在上面的方法中使用 time.After() 来模拟一个需要两秒钟来处理请求的函数。如果context 在2秒内被取消时ctx.Done() 的 channel 就会接收到空的struct 结构,就会退出函数。

func handler(ctx context.Context){
    if ctx.Err() != nil {
        fmt.Println("context is Cancelled")
        return
    }
     fmt.Println("request processed")
}

在执行一些关键业务逻辑时可以检查 ctx.Err() 的错误。如果 context被取消了,上面的函数就会暂停并返回。

发出取消事件

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

context 模块提供了三个返回 CancelFunc 的函数。

调用 cancelFunc 会向 ctx.Done() 通道发出一个空结构体,并通知正在侦听它的下游函数。

传递请求范围数据

func WithValue(parent Context, key, val interface{}) Context

像传递变量一样,请求范围数据可以使用 WithValue 函数标记该变量。WithValue 函数将值添加到 ctx 变量中的键,而 Value 函数检索给定键的值。