有没有更简洁的方法来创建一个在接收到通道上的操作后取消的上下文?

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

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 &lt;-chan bool) {
    ctx, cancel := context.WithCancel(context.Background())
    go func() {
        select {
        case &lt;-ctx.Done():
        case &lt;-stop:
            cancel()
        }
    }()
    longRunningFunction(ctx)
}

The intended control flow is as follows:

  • If the task runs to completion, it will cancel the context, the &lt;-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 &lt;-chan bool) {
    ctx := context.TODO() // because in the future, you might pass a ctx arg to this function, from which you could then &quot;inherit&quot;
    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 &lt;-ctx.Done():
        case &lt;-stop:
            cancel()
        }
    }()
    longRunningFunction(ctx)
}

huangapple
  • 本文由 发表于 2017年4月16日 15:41:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/43434939.html
匿名

发表评论

匿名网友

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

确定