为什么这段 Go 代码不会造成死锁?

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

import (
    "fmt"
    "sync"
    "time"
)

var wg sync.WaitGroup

func main() {
    ch := make(chan int) // Declare the channel inside main()

    wg.Add(4)
    go sender(ch, 42)
    go receiver(ch)

    go sender(ch, 7)
    go receiver(ch)

    wg.Wait()
}

func receiver(ch <-chan int) {
    time.Sleep(5 * time.Second)
    i := <-ch
    fmt.Printf("RECEIVING %v \n", i)
    wg.Done()
}

func sender(ch chan<- int, i int) {
    ch <- i
    wg.Done()
}

到目前为止,我对 go 中无缓冲通道的了解是

如果通道没有缓冲,发送方将阻塞,直到接收方从通道读取数据。如果发送者在接收者读取第一条消息之前尝试发送另一条消息,它将被无限期地阻塞,从而导致死锁。

这不是真的吗?

现在,我希望这段代码会出现死锁,如

启动了两个发送者协程(sender(ch, 42) 和 sender(ch, 7))。 启动了两个接收器 goroutine (receiver(ch))。 每个发送者 goroutine 在 unbuffered 通道 ch 上发送一个值。 每个接收器 goroutine 在尝试从通道读取数据之前都会等待 5 秒。

发送者 goroutine 立即发送它们的值,但接收者 goroutine 在尝试从通道读取之前会等待 5 秒。由于通道是无缓冲的,这会导致发送者 goroutine 无限期地阻塞,从而导致死锁。

任何人都可以解释一下,为什么上面的代码不会造成死锁?

Chat-gpt 帮不上忙!无法得到满意的解释。

只是添加,我可以使用

创建死锁
wg.Add(3)
go sender(ch, 42)
go receiver(ch)
go sender(ch, 7)

提前致谢。

go concurrency channel goroutine
1个回答
0
投票

如果通道没有缓冲,发送方将阻塞,直到接收方从通道读取数据。

正确。严格来说,发送方将阻塞,直到接收方“准备好”读取,因为在无缓冲通道的情况下,读取直接从发送方 goroutine 进行到接收方 goroutine。 (请参阅语言规范。)

如果发送者在接收者读取第一条消息之前尝试发送另一条消息,它将被无限期地阻塞,从而导致死锁。

第二个发送者确实会被阻塞,但不会无限期地被阻塞,并且不会发生死锁,因为不满足死锁的条件——等待同一共享资源的不同 goroutine 之间不存在相互依赖。一旦接收者醒来并从通道接收数据,至少有一个发送者 goroutine 能够继续进行。

发送者 goroutine 立即发送它们的值,但接收者 goroutine 在尝试从通道读取之前会等待 5 秒。由于通道是无缓冲的,这会导致发送者 goroutine 无限期地阻塞,从而导致死锁。

发送者实际上不会立即发送他们的值,因为通道是无缓冲的。它们必须等待接收器从通道读取数据,此时值将通过通道传送。这是一个微妙的区别,但它可能有助于澄清您的理解。

如上所述,死锁的条件不满足,因为 goroutine 之间不存在循环依赖。确实,睡眠 Goroutine 此时不可运行,但当计时器返回时它将变得可运行。当存在挂起的计时器时,Go 的死锁检测器将退出,如您在

中看到的那样。

© www.soinside.com 2019 - 2024. All rights reserved.