由于在开放通道上测距而发生死锁,但未引发恐慌

Deadlock Happens Due To Ranging Over Open Channel But Panic Not Thrown

提问人:Mitlandir 提问时间:7/13/2023 最后编辑:Jonathan HallMitlandir 更新时间:7/14/2023 访问量:64

问:

我有这段代码,我故意写了这些代码,以引起死锁和随后的恐慌抛出:

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("Start:", time.Now())
    channel1 := make(chan string, 2)

    channel1 <- "Cat"
    channel1 <- "Dog"

    limiter := time.NewTicker(time.Millisecond * 500)
    for channel1Item := range channel1 {
        <-limiter.C
        fmt.Println(channel1Item)
    }

    fmt.Println("End:", time.Now())
}

在某种程度上,死锁确实发生了,代码只是无限期地挂起,但是,它不会引发恐慌。如果我删除限制器(代码),它就会这样做。为什么股票行情可以防止恐慌?

我目前正在学习围棋,在更改随机位以满足我的好奇心时,我不小心偶然发现了这个例子。奇怪的行为(没有恐慌)发生在我决定不关闭频道看看会发生什么之后。

我知道在未被喂食的开放通道上进行测距通常会引发恐慌,但似乎在循环中有一个股票行情会中断恐慌投掷行为。

GO 通道 代码 恐慌

评论


答:

3赞 ed_Chris 7/13/2023 #1

因为 GO 中的判断是,当一个 goroutine 试图在封闭的通道上发送有价值的消息时,会引发恐慌。

在您的代码中,您没有关闭 channel1,因此循环将无限期地等待 channel1 接受该值。 <-限制器。C 将定期向通道发送值 因此,channel1 不会排除恐慌,带有限制器的循环将挂起 即使他没有任何新的价值

希望对您有所帮助

评论

0赞 jub0bs 7/13/2023
你写道:当一个 goroutine 试图在封闭的通道上发送或接收有价值的消息时,它会引发恐慌。尝试发送到封闭通道确实会触发恐慌,但允许从封闭通道接收,并且不会触发恐慌。
1赞 Brits 7/14/2023 #2

我知道在没有喂食的开放通道上测距通常会引起恐慌

我想这是正确的,如果“在开放通道上测距”是唯一正在发生的事情(或者所有其他 goroutines 也被阻止),则将检测到死锁(并触发恐慌)。将在此 (playground) 中检测到死锁:

func main() {
    c := make(chan int)
    for _ = range c {
    }
}

但不是在这个(游乐场)中:

func main() {
    go func() {
        for {
            time.Sleep(time.Nanosecond)
        }
    }()

    c := make(chan int)
    for _ = range c {
    }
}

这是因为,虽然被阻塞,但 goroutine 没有被阻塞(所以没有死锁)。for _ = range c {

识别死锁的代码相对简单,如果没有可以运行的内容,就会触发崩溃。它不会检查正在运行的代码是否正在执行有用操作!

根据 Ticker 的文档:

股票行情将调整时间间隔或下降滴答声以弥补慢速接收器。

无论频道上是否有内容正在收听,股票行情都将继续运行;它类似于在代码 (playground) 中添加如下内容:

channel2 := make(chan time.Time, 1)
go func() {
    for {
        time.Sleep(time.Millisecond * 500)
        select {
        case channel2 <- time.Now():
        default:
        }
    }
}()

无论是否有内容正在侦听,此循环都将继续运行。因此,不会检测到死锁(也不会触发恐慌)。添加具有相同的影响;正在运行的事实意味着不会检测到死锁。channel2TickerTicker