Golang 中的取消模式

Cancellation pattern in Golang

引用自50 Shades Of Go: Traps, Gotchas and Common mistakes

You can also use a special cancellation channel to interrupt the workers.

func First(query string, replicas ...Search) Result {  
    c := make(chan Result)
    done := make(chan struct{})
    defer close(done)
    searchReplica := func(i int) { 
        select {
        case c <- replicas[i](query):
        case <- done:
        }
    }
    for i := range replicas {
        go searchReplica(i)
    }

    return <-c
}

据了解,这意味着我们使用通道done提前中断worker,而不是等待完全执行(在我们的例子中执行replicas[i](query)。因此,我们可以接收最快工人 ("First Wins Pattern") 的结果,然后取消所有其他工人的工作并节省资源。

另一方面,根据 specification

For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement.

据我了解,这意味着我们不能interrupt the workers,因为在任何情况下,所有工作人员都会评估函数replicas[i]query,然后才select case <- done 和完成他们的执行。

能否请您指出我推理中的错误?

你的推理是正确的,网站上的措辞并不完全清楚。这个 "construct" 实现的是 goroutines 不会永远挂起,但是一旦搜索完成,goroutines 就会正常结束。那里什么也没有发生。

一般来说,你不能从外部中断任何 goroutine,goroutine 本身必须支持某种终止(例如关闭通道,context.Context 等)。参见

所以是的,在您发布的示例中,将同时启动所有搜索,最快的结果将在到达时返回,其余 goroutine 将继续 运行 只要他们的搜索已经完成。

剩下的怎么办?其余的将被丢弃(case <- done 将被选中,因为无缓冲通道不能容纳任何元素,并且不会有其他人从该通道接收更多元素)。

你可以在这个Go Playground example中验证这一点。