没有取消传播的上下文

时间:2019-01-10 12:33:35

标签: go asynchronous goroutine cancellation

如何创建Go上下文的副本(如果需要,则为副本),该副本包含原始文件中存储的所有值,但是原始文件不会被取消?

在我看来,这确实是一个有效的用例。假设我有一个http请求,并且在响应返回给客户端之后取消了它的上下文,并且我需要在此请求的结尾处在一个单独的goroutine中运行一个异步任务,该例程很可能会超过父上下文。

func Handler(ctx context.Context) (interface{}, error) {
        result := doStuff(ctx)
        newContext := howDoICloneYou(ctx)
        go func() {
                doSomethingElse(newContext)
        }()
        return result
}

任何人都可以建议应该怎么做吗?

当然,我可以跟踪可能放在上下文中的所有值,创建一个新的背景ctx,然后仅遍历每个可能的值并进行复制...但这似乎很乏味,并且很难在一个大型代码库。

2 个答案:

答案 0 :(得分:5)

由于context.Context是一个接口,因此您只需创建自己的实现就永远不会取消它:

import (
    "context"
    "time"
)

type noCancel struct {
    ctx context.Context
}

func (c noCancel) Deadline() (time.Time, bool)       { return time.Time{}, false }
func (c noCancel) Done() <-chan struct{}             { return nil }
func (c noCancel) Err() error                        { return nil }
func (c noCancel) Value(key interface{}) interface{} { return c.ctx.Value(key) }

// WithoutCancel returns a context that is never canceled.
func WithoutCancel(ctx context.Context) context.Context {
    return noCancel{ctx: ctx}
}

答案 1 :(得分:4)

  

任何人都可以建议应该怎么做吗?

是的。不要这样做。

如果您需要其他上下文,例如为您的异步后台任务创建一个上下文。您传入的上下文和您的后台任务之一是不相关,因此您不得尝试重用传入的上下文。

如果不相关的新上下文需要原始数据,请执行以下操作:复制所需内容并添加新内容。