提问人:Mitlandir 提问时间:7/13/2023 最后编辑:Jonathan HallMitlandir 更新时间:7/14/2023 访问量:64
由于在开放通道上测距而发生死锁,但未引发恐慌
Deadlock Happens Due To Ranging Over Open Channel But Panic Not Thrown
问:
我有这段代码,我故意写了这些代码,以引起死锁和随后的恐慌抛出:
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 中的判断是,当一个 goroutine 试图在封闭的通道上发送有价值的消息时,会引发恐慌。
在您的代码中,您没有关闭 channel1,因此循环将无限期地等待 channel1 接受该值。 <-限制器。C 将定期向通道发送值 因此,channel1 不会排除恐慌,带有限制器的循环将挂起 即使他没有任何新的价值
希望对您有所帮助
评论
我知道在没有喂食的开放通道上测距通常会引起恐慌
我想这是正确的,如果“在开放通道上测距”是唯一正在发生的事情(或者所有其他 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:
}
}
}()
无论是否有内容正在侦听,此循环都将继续运行。因此,不会检测到死锁(也不会触发恐慌)。添加具有相同的影响;正在运行的事实意味着不会检测到死锁。channel2
Ticker
Ticker
评论