在Golang中,当上下文变量超出作用域时,Context.Done()是否解除阻塞?

huangapple go评论85阅读模式
英文:

Does Context.Done() unblock when context variable goes out of scope in golang?

问题

context.Done()会在上下文变量超出作用域且未显式调用取消时解除阻塞吗?

假设我有以下代码:

func DoStuff() {
    ctx, _ := context.WithCancel(context.Background())
    go DoWork(ctx)
    return
}

在DoStuff()中的return语句之后,ctx.Done()会在DoWork()中解除阻塞吗?

我在这个线程中找到了相关讨论:https://groups.google.com/forum/#!topic/golang-nuts/BbvTlaQwhjw,其中提问者询问如何使用Context.Done(),声称当上下文变量离开作用域时,Context.Done()会解除阻塞,但没有人验证过这一点,而且我在文档中也没有找到相关说明。

英文:

Will context.Done() unblock when a context variable goes out of scope and cancel is not explicitly called?

Let's say I have the following code:

func DoStuff() {
    ctx, _ := context.WithCancel(context.Background())
    go DoWork(ctx)
    return
}

Will ctx.Done() unblock in DoWork after the return in DoStuff()?

I found this thread, https://groups.google.com/forum/#!topic/golang-nuts/BbvTlaQwhjw, where the person asking how to use Context.Done() claims that context.Done() will unblock when the context variable leaves scope but no one validated this, and I didn't see anything in the docs.

答案1

得分: 6

不,当上下文离开作用域时,它不会自动取消。通常,人们会自己调用defer cancel()(使用ctx.WithCancel()返回的回调函数),以确保上下文被取消。

https://blog.golang.org/context 提供了如何正确使用上下文的很好概述(包括上面提到的defer模式)。此外,源代码https://golang.org/src/context/context.go 非常易读,你可以看到没有任何魔法可以提供自动取消功能。

英文:

No, it doesn't cancel automatically when the context leaves scope. Typically one calls defer cancel() (using the callback from ctx.WithCancel()) oneself to make sure that the context is cancelled.

https://blog.golang.org/context provides a good overview of how to use contexts correctly (including the defer pattern above). Also, the source code https://golang.org/src/context/context.go is quite readable and you can see there's no magic that would provide automatic cancellation.

答案2

得分: 2

"Unblocking"这个术语并不是很清晰。Done()方法返回一个通道(或nil),当上下文被"取消"时,该通道将接收一个struct{}或关闭。该通道的具体含义,以及何时发送数据,取决于具体的实现。它可以像WithDeadline一样在某个固定时间发送/关闭,也可以像WithCancel一样手动发送/关闭。

关键是,这个过程从来不是"自动"的,也不能保证一定会发生。如果你使用WithCancel创建一个上下文,并从Done()通道读取数据,那么该读取操作将会无限期地阻塞,直到调用Cancel()方法。如果永远不调用该方法,那么你将会有一个无用的goroutine,并且每次执行该操作时,应用程序的内存都会增加。

一旦上下文完全超出范围(没有执行的goroutine监听它,也没有对父上下文的引用),它将被垃圾回收,所有相关资源都会被释放。

编辑:不过在阅读源代码后,我发现WithCancel等函数会启动goroutine来传播取消信号。因此,你必须确保在某个时刻调用Cancel方法,以避免goroutine泄漏。

英文:

"Unblocking" is not the clearest terminology. Done() returns a channel (or nil) that will receive a struct{} and/or close when the context is "cancelled". What exactly that chan is, or when it is sent on, is up to the individual implementation. It may be sent/closed at some fixed time as with WithDeadline, or manually done as with WithCancel.

The key though, is that this is never "automatic" or guaranteed to happen. If you make a context with WithCancel and read from the Done() channel, that read will block indefinitely until the Cancel() method is called. If that never happens, then you have a wasted goroutine and your application's memory will increase each time you do it.

Once the context is completely out of scope (no executing goroutine is listening to it or has a reference to the parent context), it will get garbage collected and everything will go away.

EDIT: After reading the source though, it looks like WithCancel and friends spawn goroutines to propigate the cancellation. Therefore you must make sure Cancel gets called at some point to avoid goroutine leaks.

huangapple
  • 本文由 发表于 2017年5月9日 01:56:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/43854294.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定