以下 Go 代码示例在行 c <- byte(0)
之间有一个竞争条件和 close(c)
.当使用 go test -race
运行代码时会发出信号.
func TestRace(t *testing.T) {
var c = make(chan byte, 20)
go func() {
defer func() {
if r := recover(); r == nil {
t.Error("expected panic error")
}
}()
for i := 0; i < 25; i++ {
c <- byte(0)
}
t.Error("expected a panic")
}()
close(c)
}
如何避免这种竞争情况?
编辑:根据 Icza 在他的评论中的建议,这里是解决方案:
func TestRace(t *testing.T) {
var c = make(chan byte, 20)
var done = make(chan struct{})
go func() {
for i := 0; i < 25; i++ {
select{
case c <- byte(0):
case <-done:
close(c)
return
}
}()
close(done)
}
这不会有竞争条件,而且是干净的。这是一个愚蠢的简单例子。有人告诉我 select 会增加开销,但我没有研究它,因为它与我的用例无关。
最佳答案
通常,在 channel 上发送值的 goroutine 负责关闭它。关闭 channel 基本上是一个信号,表示将(不能)在其上发送更多值。
您没有这样做:您的新 goroutine 是在其上发送值的 goroutine,而您的另一个 goroutine 是关闭它的 goroutine。
要摆脱竞争条件,只需按预期使用 channel :将 close(c)
调用移动到在其上发送值的 goroutine,例如:
go func() {
defer func() {
if r := recover(); r == nil {
fmt.Println("expected panic error")
}
}()
for i := 0; i < 25; i++ {
c <- byte(0)
}
close(c)
fmt.Println("expected a panic")
}()
for x := range c {
fmt.Println("Received:", x)
}
在 Go Playground 上试试.
关于关闭 Go channel 时的 Go 竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47714470/