是否有更简洁的方法来创建在通道上接收后取消的上下文?

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)
}

预期的控制流程如下:

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

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

修复方法如下(主要是的实现):

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)
}