Golang Context 是什么

Go 1.7 标准库引入 context 包,中文翻译为 “上下文”,准确说它是 goroutine 的上下文,它包含 goroutine 的运行状态、环境、现场等信息。

context 主要用来在 goroutine 之间传递上下文信息,包括:取消信号、超时时间、截止时间、共享数据等。

随着 context 包的引入,标准库中很多接口加上了 context.Context 参数,例如 database/sql 包。context 几乎成为了并发控制和超时控制的标准做法。

context.Context 类型的值可以协调多个 groutine 中的代码执行取消操作,共享键值数据,而且它是并发安全的。与其它协作的 API 都可以由外部控制执行取消操作,例如:取消一个 HTTP 请求的执行。

context 包位于 src/context/context.go 源码文件中。

context.Context 接口的源码:

// Context携带一个跨越API边界的截止日期、取消信号和请求范围的值。
// 它的方法是并发安全的,可以同时被多个 goroutin 使用。
type Context interface {
    // Done 返回一个通道,该通道在上下文被取消或超时时关闭。
    Done() <-chan struct{}

    // Err 指示关闭完成通道后,取消上下文的原因。
    Err() error

    // Deadline 返回该上下文将被取消的截止时间(如果有的话)。
    Deadline() (deadline time.Time, ok bool)

    // Value返回与 key 关联的值,如果没有则返回 nil。
    Value(key interface{}) interface{}
}

使用 Context 的控制协程的简单范例。调用者 main 函数,启动运行一个协程 contextTest, 3秒后主动取消 contextTest 的运行。

package main

import (
	"context"
	"time"
)

func contextTest(ctx context.Context) {
    cancelFlag := false // 取消标志

    // 协程,用于监控是否被其它协程通知取消
    go func(ctx context.Context) {
        select {
            case <- ctx.Done():
            cancelFlag = true
            }
    }(ctx)

    for {
        // 检测取消标记,一旦为真,就结束协程
        if cancelFlag {
            println("goroutin finished")
            return
        }

        // 每隔 1 秒钟,打印 running
        time.Sleep(time.Second)
        println("running")
    }
}

func main() {
    // 返回值 cancelFunc 是一个函数,用于取消运行中的协程
    ctx, cancelFunc := context.WithCancel(context.Background())
    go contextTest(ctx)

    // 让contextTest 协程运行 3 秒钟,然后调用取消函数
    time.Sleep(3*time.Second)
    println("send a closed signal")
    cancelFunc()

    // 等待 3 秒钟,让 contextTest 协程优雅结束。
    time.Sleep(3*time.Second)
}

Golang Context 的作用和用法:context 主要用来在 goroutine 之间传递上下文信息,包括:取消信号、超时时间、截止时间、共享数据等。在 Go 语言程序中,关闭协程可以通过 channel+select 方式实现, ...