chm*_*ike 1 concurrency channel go race-condition goroutine
以下Go代码示例在行c <- byte(0)和之间具有Race条件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)
}
Run Code Online (Sandbox Code Playgroud)
我怎样才能避免这种竞争条件?
编辑:根据他的评论中的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)
}
Run Code Online (Sandbox Code Playgroud)
这不会有竞争条件而且会很干净.这是一个愚蠢的简单例子.我被告知select会增加开销,但我没有调查它,因为它与我的用例无关.
通常,在通道上发送值的goroutine负责关闭它.关闭一个频道基本上是一个信号,不会再发送任何值(可以).
你没有这样做:你的新goroutine是发送它的值,而你的另一个goroutine是关闭它的那个.
要摆脱竞争条件,只需按原样使用通道:将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)
}
Run Code Online (Sandbox Code Playgroud)
在Go Playground尝试一下.