【问题标题】:Is there a more concise way of creating a context that is cancelled after receiving on a channel?是否有更简洁的方法来创建在频道接收后取消的上下文?
【发布时间】:2017-04-17 02:13:04
【问题描述】:

我需要调用一个以Context 作为参数的函数。此代码块可以访问一个通道,该通道用于发出应该取消操作的信号。

这是我目前用来在收到值时取消 Context 的方法:

func doSomething(stop <-chan bool) {
    ctx, cancel := context.WithCancel(context.Background())
    go func() {
        select {
        case <-ctx.Done():
        case <-stop:
            cancel()
        }
    }()
    longRunningFunction(ctx)
}

预期的控制流程如下:

  • 如果任务运行完成,它将取消上下文,&lt;-ctx.Done() 将触发,goroutine 将终止。

  • 如果在stop 上接收到值,则取消上下文,通知任务它应该退出。再次,当这种情况发生时,goroutine 将终止。

这似乎过于复杂。有没有更简单的方法来完成预期的行为?

【问题讨论】:

  • 如果您的实际代码运行良好,这可能更适合codereview.stackexchange.com
  • @oliverpool 我做了,它很快就关闭了,他们建议我在这里问。
  • 我认为你在doSomething 中需要defer cancel(),否则当longRunningFunction 完成而不被stop 频道取消时,你会泄漏gorutine。您可能还应该使用context.TODO() 而不是context.Background(),因为从长远来看,您希望用上下文替换停止通道。

标签: go channel


【解决方案1】:

正如@ain 提到的,如果longRunningFunction 运行到最后并且在stop 上没有发送任何内容(或者它没有关闭),您的代码当前会泄漏goroutine:select 语句将永远不会被执行(完成context 的唯一方法是当stop 出现某些事情时调用cancel)。

这里有一个修复方法(主要是@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)
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-11
    • 2019-09-30
    • 2011-08-08
    • 2021-08-05
    • 1970-01-01
    • 1970-01-01
    • 2020-10-14
    • 2016-06-19
    相关资源
    最近更新 更多