英文:
Is there a more concise way of creating a context that is cancelled after receiving on a channel?
问题
我需要调用一个以 Context
作为参数的函数。这段代码可以访问一个用于发出取消操作信号的通道。
以下是我目前使用的代码来在接收到值时取消 Context
:
func doSomething(stop <-chan bool) {
ctx, cancel := context.WithCancel(context.Background())
go func() {
select {
case <-ctx.Done():
case <-stop:
cancel()
}
}()
longRunningFunction(ctx)
}
预期的控制流程如下:
-
如果任务正常完成,它将取消上下文,
<-ctx.Done()
将触发,goroutine 将终止。 -
如果在
stop
上接收到一个值,上下文将被取消,通知任务应该退出。再次,当发生这种情况时,goroutine 将终止。
这似乎过于复杂。有没有更简单的方法来实现预期的行为?
英文:
I need to invoke a function that takes a Context
as a parameter. This block of code has access to a channel that is used for signalling that the operation should be cancelled.
Here is what I am currently using to cancel the Context
when a value is received:
func doSomething(stop <-chan bool) {
ctx, cancel := context.WithCancel(context.Background())
go func() {
select {
case <-ctx.Done():
case <-stop:
cancel()
}
}()
longRunningFunction(ctx)
}
The intended control flow is as follows:
-
If the task runs to completion, it will cancel the context, the
<-ctx.Done()
will fire, and the goroutine will terminate. -
If a value is received on
stop
, the context is cancelled, notifying the task that it should quit. Once again, the goroutine will terminate when this happens.
This seems overly complex. Is there a simpler way to accomplish the intended behavior?
答案1
得分: 1
如@ain所提到的,你的代码目前存在一个问题,如果longRunningFunction
运行到最后,stop
上没有发送任何内容(或者没有关闭),那么goroutine将会泄漏:select
语句将永远不会被执行(只有当stop
上有内容出来调用cancel
时,context
才会完成)。
以下是修复这个问题的方法(主要是实现了@ain的评论):
func doSomething(stop <-chan bool) {
ctx := context.TODO() // 因为将来你可能会从这个函数传递一个ctx参数,从中你可以“继承”
ctx, cancel := context.WithCancel(ctx)
defer cancel() // 确保无论发生什么都释放相关资源(并防止以下goroutine泄漏)
go func() {
select {
case <-ctx.Done():
case <-stop:
cancel()
}
}()
longRunningFunction(ctx)
}
希望对你有帮助!
英文:
As @ain mentionned, your code currently leaks the goroutine if the longRunningFunction
runs to the end and nothing is sent on stop
(or it is not closed): the select
statement will never be fulfilled (the only way for the context
to be done is when something comes out of stop
to call cancel
).
Here is a way to fix it (mainly an implementation of @ain's comment):
func doSomething(stop <-chan bool) {
ctx := context.TODO() // because in the future, you might pass a ctx arg to this function, from which you could then "inherit"
ctx, cancel := context.WithCancel(ctx)
defer cancel() // to be sure to release the associated resources whatever happens (and prevent the following goroutine from leaking)
go func() {
select {
case <-ctx.Done():
case <-stop:
cancel()
}
}()
longRunningFunction(ctx)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论