没有取消传播的上下文
Posted
技术标签:
【中文标题】没有取消传播的上下文【英文标题】:Context without cancel propagation 【发布时间】:2019-06-05 07:57:41 【问题描述】:我如何创建一个 Go 上下文的副本(如果你愿意的话是一个克隆),它包含存储在原始文件中的所有值,但不会在原始文件中被取消?
对我来说,这确实是一个有效的用例。假设我有一个 http 请求,并且它的上下文在响应返回给客户端后被取消,我需要在这个请求结束时在一个单独的 goroutine 中运行一个异步任务,这个 goroutine 很可能会超过父上下文。
func Handler(ctx context.Context) (interface, error)
result := doStuff(ctx)
newContext := howDoICloneYou(ctx)
go func()
doSomethingElse(newContext)
()
return result
谁能建议这应该怎么做?
当然,我可以跟踪可能放入上下文中的所有值,创建一个新的背景 ctx,然后遍历每个可能的值并复制...但这看起来很乏味,而且很难在大型代码库。
【问题讨论】:
只要你不在 aync goroutine 中的 context 的 done 通道上监听,你应该没问题,对吧? 可以实现自己的context.Context
接口实现。
@NestorSokil 然后它应该创建自己的新上下文并将其传播给孩子,而不是使用可能不相关的请求上下文
@NestorSokil 手动复制密钥有什么危害吗?
@Nestor Sokil 我的意思是play.golang.org/p/6WeRs-LMjh6
【参考方案1】:
由于 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 noCancelctx: ctx
【讨论】:
如果你在那个上下文中传递值,你会遇到一个大问题,因为如果父上下文已经被取消,子上下文可能不会取消操作,但会尝试读取父上下文中的值,可能已经被垃圾回收了。 只要这个上下文在父级周围就不是垃圾,根据定义,因为仍然有对它的引用。访问垃圾收集的值是矛盾的。【参考方案2】:谁能建议这应该怎么做?
是的。不要这样做。
如果您需要不同的上下文,例如然后为您的异步后台任务创建一个 new 上下文。您的传入上下文和您的一项后台任务不相关,因此您不得尝试重用传入的上下文。
如果不相关的新上下文需要原始上下文中的一些数据:复制您需要的内容并添加新内容。
【讨论】:
原始上下文中的值如何?有问题的代码库的工作方式是其中有很多。而且我不想跟踪每个创建新上下文的情况并调查下游代码可能需要哪些值。 @NestorSokil 那么 context.Context 不是您数据的适当传输机制。您可以使用上下文,这包括取消,如果您不想传播取消,则必须创建一个新上下文。 我同意整个概念可能是错误的,但正如我所说,代码库在很大程度上依赖于上下文。 感谢您的意见,但我的具体问题应该通过彼得的回答来解决 异步http处理程序的情况如何?返回 202 并从处理程序返回后,请求的上下文将很快被取消。您可能需要异步 goroutine 中的上下文来完成工作。如果服务器具有依赖于向上下文写入和读取请求范围数据的中间件(例如日志记录),则可能需要来自请求上下文的值。处理程序本身当然不应该知道所有这些值(中间件的内部),以便它可以将它们复制到新的上下文中。以上是关于没有取消传播的上下文的主要内容,如果未能解决你的问题,请参考以下文章