关闭Go频道

时间:2017-12-08 12:30:27

标签: go concurrency race-condition channel goroutine

以下Go代码示例在行c <- byte(0)close(c)之间有一个Race条件。当代码与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会增加开销,但我没有调查它,因为它与我的用例无关。

1 个答案:

答案 0 :(得分:5)

通常,在通道上发送值的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)
}

Go Playground上尝试。