首页 文章

关闭Go Channels 时进入竞争状态

提问于
浏览
1

以下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)
}

我怎样才能避免这种竞争条件?

Edit: 根据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 回答

  • 5

    通常,在通道上发送值的goroutine负责关闭它 . 关闭 Channels 基本上是一个信号,不会再发送任何值(可以) .

    你没有这样做:你的新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 .

相关问题