← 中文资源

简体中文版本

上下文取消传播

用 Go 的 context 在调用树中传播取消与截止时间,使被放弃的工作及时停止。

阅读英文原文

功能说明

该示例把取消与截止时间作为一等参数沿调用链传递。fetch 把实际工作放到独立 goroutine 执行,并用 select 在结果与上下文完成之间等待;一旦上下文被取消或超时,调用立即返回,而工作 goroutine 因持有同一上下文也应在检查取消后尽快退出。

关键代码

结果通道容量为一,保证即便调用方在上下文先完成而不再接收时,工作 goroutine 也能写入而不泄漏——若通道无缓冲,它会被永久阻塞。WithDeadline 只是对标准库 WithTimeout 的封装,强调截止时间由父上下文派生,取消能向下传播。返回专门的 ErrTimeout,让调用方区分超时与业务错误。

使用注意

取消是协作式的:work 内部必须在合适位置检查 ctx.Done() 或把 ctx 继续传给下游,否则 goroutine 会继续运行直至自然结束,取消形同虚设。不要在取消后假设副作用已经回滚,写入类工作应保证幂等或自行处理部分完成。永远应当调用返回的 cancel 函数以释放定时器与关联资源,即便超时已经发生。

代码示例

package request

import (
    "context"
    "errors"
    "time"
)

var ErrTimeout = errors.New("request timed out")

type Result struct {
    Value int
    Err   error
}

func fetch(ctx context.Context, work func(context.Context) (int, error)) (int, error) {
    resultCh := make(chan Result, 1)
    go func() {
        value, err := work(ctx)
        resultCh <- Result{Value: value, Err: err}
    }()
    select {
    case <-ctx.Done():
        return 0, ErrTimeout
    case result := <-resultCh:
        return result.Value, result.Err
    }
}

func WithDeadline(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc) {
    return context.WithTimeout(parent, timeout)
}