2 回答

TA贡献1811条经验 获得超4个赞
这似乎是不可能的,因为cancel()
它不是主 goroutine 中的阻塞操作。正因为如此,当select
解除阻塞时,可能会有多种情况可用,并且没有办法让一个渠道优于另一个渠道。任何类型的 check-channel-then-write 方案都将是活泼的,因为在检查后可以取消上下文。
使用done
通道并写入它而不是上下文取消将起作用,因为写入done
通道将是主 goroutine 的阻塞操作,并且 select 将始终有一个活动案例。

TA贡献1876条经验 获得超7个赞
请注意,这是一个更新的答案,因为原始答案存在问题。
正如其他人所指出的,如果没有额外的同步,您将无法避免竞争条件。您可以使用 Mutex,但sync.Cond看起来很合适。在下面的代码中,接收 goroutine 表示它已从 chan 接收到值。它在发出信号之前取消上下文(使用Cond.Signal),发送 goroutine 等待信号。这避免了竞争条件,因为上下文状态在被检查之前已更新。
ctx, cancel := context.WithCancel(context.Background())
ch := make(chan int)
cond := sync.NewCond(&sync.Mutex{}) // *** new
go func() {
defer close(ch)
cond.L.Lock() // *** new
defer cond.L.Unlock() // *** new
for i := 1; ; i++ {
ch <- i // *** moved
cond.Wait() // *** new
if ctx.Err() != nil { // *** changed
return
}
}
}()
print(<-ch)
cond.Signal() // *** new
print(<-ch)
cond.Signal() // *** new
print(<-ch)
cancel()
cond.Signal() // *** new
print(<-ch)
cond.Signal() // *** new
这是我能看到的最简单的方法,接收 goroutine 在取消上下文后将不会在通道上接收任何值。
- 2 回答
- 0 关注
- 102 浏览
添加回答
举报