为什么这种可互换打印的代码会导致死锁?

问题描述 投票:0回答:1

这是一段使用Go的通道交替打印1到10的代码,但是这样写会导致死锁。

func main() {
    oddCh := make(chan struct{})
    evenCh := make(chan struct{})
    done := make(chan struct{})

    go func() {
        defer close(evenCh)
        for i := 1; i < 10; i += 2 {
            <-oddCh
            fmt.Println(i)
            evenCh <- struct{}{}
        }
    }()

    go func() {
        defer close(done)
        for i := 2; i <= 10; i += 2 {
            <-evenCh
            fmt.Println(i)
            oddCh <- struct{}{}
            if i == 10 {
                close(oddCh)
                break
            }
        }
    }()

    oddCh <- struct{}{}

    <-done
}

输出:

1
2
3
4
5
6
7
8
9
10
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    D:/Gocode/alternateprint/main.go:34 +0x125

goroutine 7 [chan send]:
main.main.func2()
    D:/Gocode/alternateprint/main.go:24 +0xcb
created by main.main in goroutine 1
    D:/Gocode/alternateprint/main.go:19 +0x105

我认为第一个goroutine执行到i=9后唤醒第二个goroutine,然后第二个goroutine执行到i=10并打印10,向oddCh写入信号,然后关闭oddCh。这不是一个大问题,但它确实会导致死锁。然后我只是交换了两段的顺序:

if i == 10 {
    close(oddCh)
    break
}

oddCh <- struct{}{}

交换后没有死锁,输出正常。

go deadlock
1个回答
0
投票

没有与事件循环中的最终

oddCh <- struct{}{}
相对应的读取。在全局范围内,有 6 次写入
oddCh
,而只有 5 次读取,因此最终的写入必须永远阻塞。你的程序等待
done
,第二个(偶数)goroutine 负责在退出时关闭该通道,因此阻塞第二个 goroutine 会导致你的程序死锁。

如果您将

if i == 10 { close(oddCh); break }
放在该代码中,那么
oddCh
将关闭(不会阻塞)并且 goroutine 退出。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.