英文:
Runtime: goroutine blocks forever
问题
我有这段 Golang 代码:
package main
import (
"fmt"
"time"
)
func main() {
burstyLimiter := make(chan time.Time, 3)
for i := 1; i <= 3; i++ {
burstyLimiter <- time.Now()
}
go func() {
for t := range time.Tick(200 * time.Millisecond) {
burstyLimiter <- t
}
}()
burstyRequests := make(chan int, 5)
for i := 1; i <= 5; i++ {
burstyRequests <- i
}
for r := range burstyRequests {
fmt.Println("received request", r, time.Now())
}
}
当我运行这段代码时,它会永远阻塞,如下所示:
然后我有另一个稍微修改过的版本:
package main
import (
"fmt"
"time"
)
func main() {
burstyRequests := make(chan int, 5)
for i := 1; i <= 5; i++ {
burstyRequests <- i
}
for r := range burstyRequests {
fmt.Println("received request", r, time.Now())
}
}
这个版本按预期会发生死锁,如下所示:
对于永远阻塞的那个版本,我的假设是由于 time.Tick 的行为可能导致泄漏,以及对通道的范围循环会在通道有可能写入时阻塞。
我希望能够清楚地了解我对这种情况的理解是否正确。
英文:
I have this bit of Golang code here
package main
import (
"fmt"
"time"
)
func main() {
burstyLimiter := make(chan time.Time, 3)
for i := 1; i <= 3; i++ {
burstyLimiter <- time.Now()
}
go func() {
for t := range time.Tick(200 * time.Millisecond) {
burstyLimiter <- t
}
}()
burstyRequests := make(chan int, 5)
for i := 1; i <= 5; i++ {
burstyRequests <- i
}
for r := range burstyRequests {
fmt.Println("received request", r, time.Now())
}
}
When I run this, it blocks forever as shown here
Then I have another slightly modified version here
package main
import (
"fmt"
"time"
)
func main() {
burstyRequests := make(chan int, 5)
for i := 1; i <= 5; i++ {
burstyRequests <- i
}
for r := range burstyRequests {
fmt.Println("received request", r, time.Now())
}
}
this version runs into a deadlock as expected as seen here
For the one which blocks forever, my assumption is due to the behavior of time.Tick which possibly results in a leak and the range over a channel which blocks as long as there’s a possibility of a write into the channel.
I would like to get help with clarity as to whether or not my understanding of this situation is correct.
答案1
得分: 1
你的主要问题是设置了一个用于从通道中提取数据的for循环,只要通道是打开的,就会一直运行,而没有关闭该通道。在前一种情况下,代码块会一直阻塞,很可能是因为它在等待从burstyLimiter
中提取数据,而该通道已经写入死锁。在后一种情况下,死锁足够简单,Go可以检测到并为您引发恐慌。要解决此问题,您需要做两件事:
- 确保通道已关闭
- 确保循环有退出的方式
以下是一些代码,应该可以帮助您完成这两个任务:
package main
import (
"fmt"
"time"
)
func main() {
burstyRequests := make(chan int, 5)
for i := 1; i <= 5; i++ {
burstyRequests <- i
}
defer close(burstyRequests)
loop:
for {
select {
case r := <-burstyRequests:
fmt.Println("received request", r, time.Now())
default:
break loop
}
}
}
这段代码首先确保在函数返回时关闭了burstyLimiter
,然后进入一个循环,在该循环中尝试从burstyLimiter
接收数据,直到无法再接收到数据为止。这是实现您想要的功能的最基本的方法。
如果您有更复杂的用例,您可以包括另一个名为quit
的通道,当您想要停止接收时向其发送,并检查是否在该通道上接收到数据,否则等待数据。或者您可以添加一个计时器。但是,无论您做什么,都需要一种方式来确保您的程序知道何时完成工作并可以关闭。
英文:
Your primary issue is that you've setup a for-loop designed to pull data out of a channel, so long as it's open, without ever closing that channel. In the former case, the code blocks forever, likely because it's waiting on data to be pulled from burstyLimiter
, which is write-deadlocked. In the latter case, the deadlock is simple enough for Go to detect it and it panics for you. To fix this, you need to do two things:
- Ensure that the channel is closed
- Ensure that the loop has a way to exit
Here's some code that should help you accomplish both of these:
package main
import (
"fmt"
"time"
)
func main() {
burstyRequests := make(chan int, 5)
for i := 1; i <= 5; i++ {
burstyRequests <- i
}
defer close(burstyRequests)
loop:
for {
select {
case r := <-burstyRequests:
fmt.Println("received request", r, time.Now())
default:
break loop
}
}
}
This code works by first ensuring that burstyLimiter
is closed when the function returns and then entering a loop wherein it attempts to receive from burstyLimiter
until no more data can be received. This is the most basic way to do what you want.
If you have a more complex use-case, you could include another channel called quit
that you send to when you want to stop receiving and then check if you receive on that or wait for data otherwise. Or you could add a timer. But, no matter what you do, you need a way to ensure that your program knows when it is done with its work and can shut down.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论