Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Context 上下文 #50

Open
kevinyan815 opened this issue Jan 25, 2021 · 0 comments
Open

Context 上下文 #50

kevinyan815 opened this issue Jan 25, 2021 · 0 comments

Comments

@kevinyan815
Copy link
Owner

kevinyan815 commented Jan 25, 2021

Context 与 Go 语言中的并发编程有着比较密切的关系,在其他语言中我们很难见到类似 Context 的东西,它不仅能够用来设置截止日期、同步『信号』还能用来传递请求相关的值。

Go 语言里每一个并发的执行单元叫做 goroutine,当一个用Go语言编写的程序启动时,main 函数在一个单独的 goroutine 中运行。main 函数返回时,所有的goroutine都会被直接打断,程序退出。除此之外如果想通过编程的方法让一个goroutine 中断其他 goroutine 的执行,只能是通过在多个 goroutine 间用 context 上下文对象同步取消信号的方式来实现。

Context 其实是 Go 语言 context 包对外暴露的接口,

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}

常用下面两个方法

  • Done 方法需要返回一个 Channel,多次调用 Done 方法会返回同一个 Channel,这个 Channel 会在工作完成或者上下文被取消之后close掉,通过关闭Channel的方式,实现通知使用该 Context 的所有goroutine。

  • Err 方法会返回当前 Context 结束的原因,它只在 Done 返回的 Channel 被关闭时才会返回非空的值;

    如果当前 Context 被取消就会返回 Canceled 错误;

    如果当前 Context 超时就会返回 DeadlineExceeded 错误;

Context一般有下面两种用法。

用法一:主动发起取消通知

使用context.WithCancel 创建一个支持取消功能的上下文。

func operation1(ctx context.Context) error {
        // 假设这个操作会因为某种原因失败
        // 使用time.Sleep来模拟一个资源密集型操作
    time.Sleep(100 * time.Millisecond)
    return errors.New("failed")
}
 
func operation2(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("canceled operation2")
            return
        // 注意:省略default分支 整个goroutine 会被阻塞住
        case default :
        }
 
 
        // 可以选择在default分支或者是这里执行业务逻辑
        time.Sleep(10 * time.Millisecond)
    }
}
 
func main() {
    // 新建一个上下文
    ctx := context.Background()
        // 在初始上下文的基础上创建一个有取消功能的上下文
    ctx, cancel := context.WithCancel(ctx)
        // 在不同的goroutine中运行operation2
    go func() {
      operation2(ctx)
    }()
   
  err := operation1(ctx)
        // 如果这个操作返回错误,取消所有使用相同上下文的 goroutine 的执行
    if err != nil {
        cancel()
    }
}

用法二:基于时间到期后自动取消通知

// 这个上下文将会在3秒后被取消
// 如果需要在到期前就取消可以像前面的例子那样使用cancel函数
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
 
// 上下文将在2009-11-10 23:00:00被取消
ctx, cancel := context.WithDeadline(ctx, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC))

worker goroutine 内部要跟上面的例子一样,通过 select case <- Done() 接收通道传递过来的取消信号。唯一的
区别是这两种Context是到期自动同步信号,不需要在 main goroutine 内主动触发

如果想在到期前提前让 worker goroutine 结束执行,调用创建Context时返回的 cancel 函数。

更多 Context 的应用示例:https://github.com/kevinyan815/gocookbook/tree/master/codes/context_demo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant