英文:
goroutine asleep - deadlock
问题
在下面的代码中,我试图生成MaxOutstanding
数量的处理程序。每个处理程序循环遍历队列queue
中的项目并将其打印出来,我还向done
通道写入true
。
在我的主函数中,我启动处理程序并向queue
写入9个元素,并等待第一个元素被写入done
队列。
package main
import "fmt"
type Request struct {
int32
}
var MaxOutstanding = 5
func handle(queue chan *Request, i int, done chan bool) {
for r := range queue {
fmt.Println(i, "---", r)
done <- true
}
}
func Serve(clientRequests chan *Request, quit, done chan bool) {
// Start handlers
for i := 0; i < MaxOutstanding; i++ {
go handle(clientRequests, i, done)
}
<-quit // Wait to be told to exit.
}
func main() {
clientRequests := make(chan *Request)
quit := make(chan bool)
done := make(chan bool)
go Serve(clientRequests, quit, done)
clientRequests <- &Request{4}
clientRequests <- &Request{1}
clientRequests <- &Request{2}
clientRequests <- &Request{3}
clientRequests <- &Request{5}
clientRequests <- &Request{6}
clientRequests <- &Request{7}
clientRequests <- &Request{8}
clientRequests <- &Request{9}
fmt.Println("...........>", <-done)
close(clientRequests)
close(done)
}
执行时,我得到以下错误。我不明白实现有什么问题,我甚至关闭了通道。
4 --- &{4}
0 --- &{1}
1 --- &{2}
2 --- &{3}
3 --- &{5}
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/home/ubuntu/digs-svc/src/digs/go1.go:45 +0x251
goroutine 5 [chan receive]:
main.Serve(0xc82004c060, 0xc82004c0c0, 0xc82004c120)
/home/ubuntu/digs-svc/src/digs/go1.go:28 +0x92
created by main.main
/home/ubuntu/digs-svc/src/digs/go1.go:37 +0xb9
goroutine 6 [chan send]:
main.handle(0xc82004c060, 0x0, 0xc82004c120)
/home/ubuntu/digs-svc/src/digs/go1.go:16 +0x23b
created by main.Serve
/home/ubuntu/digs-svc/src/digs/go1.go:25 +0x5b
编辑:
显然,fmt.Println("....", <-done)
不足以表示done
通道有一个消费者。我将代码移到了执行顺序的前面。当数据被写入通道时,消费者需要“监听”该通道。在我之前的代码中,当第一个数据被写入时,没有消费者。
工作代码。
https://play.golang.org/p/98l2M4XO9t
英文:
In the following code I am trying to spawn MaxOutstanding
number of handlers. Each handler loops over items in the queue queue
and prints them out, I also write true
to the done
channel.
In my main function, I start the handlers and write 9 elements to the queue
and wait for the 1st element to be written to the done
queue.
package main
import "fmt"
type Request struct {
int32
}
var MaxOutstanding = 5
func handle(queue chan *Request, i int, done chan bool) {
for r := range queue {
fmt.Println(i, "---", r)
done <- true
}
}
func Serve(clientRequests chan *Request, quit, done chan bool) {
// Start handlers
for i := 0; i < MaxOutstanding; i++ {
go handle(clientRequests, i, done)
}
<-quit // Wait to be told to exit.
}
func main() {
clientRequests := make(chan *Request)
quit := make(chan bool)
done := make(chan bool)
go Serve(clientRequests, quit, done)
clientRequests <- &Request{4}
clientRequests <- &Request{1}
clientRequests <- &Request{2}
clientRequests <- &Request{3}
clientRequests <- &Request{5}
clientRequests <- &Request{6}
clientRequests <- &Request{7}
clientRequests <- &Request{8}
clientRequests <- &Request{9}
fmt.Println( "...........>", <- done )
close(clientRequests)
close(done)
}
On execution I get the following error. I don't see whats wrong with the implementation, I am even closing the channel.
4 --- &{4}
0 --- &{1}
1 --- &{2}
2 --- &{3}
3 --- &{5}
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/home/ubuntu/digs-svc/src/digs/go1.go:45 +0x251
goroutine 5 [chan receive]:
main.Serve(0xc82004c060, 0xc82004c0c0, 0xc82004c120)
/home/ubuntu/digs-svc/src/digs/go1.go:28 +0x92
created by main.main
/home/ubuntu/digs-svc/src/digs/go1.go:37 +0xb9
goroutine 6 [chan send]:
main.handle(0xc82004c060, 0x0, 0xc82004c120)
/home/ubuntu/digs-svc/src/digs/go1.go:16 +0x23b
created by main.Serve
/home/ubuntu/digs-svc/src/digs/go1.go:25 +0x5b
EDIT:
Apparently, the fmt.Println("....", <- done)
wasn't enough to signify that there is a consumer to the done
channel. I moved the code up in the execution order. A consumer needs to be "listening" on the channel when data is written to it. In my earlier code, the when the first data was written there were no consumer.
Working code.
答案1
得分: 1
你的 handle
函数中的 done
通道的发送操作阻塞了对通道的迭代,因为没有任何地方接收该通道的数据。
那些额外的通道实际上没有起到任何作用,你可以添加一个 WaitGroup
来同步处理程序的退出,然后可以移除 done
通道,这样处理程序就可以继续执行。
func handle(queue chan *Request, i int, wg *sync.WaitGroup) {
defer wg.Done()
for r := range queue {
fmt.Println(i, "---", r)
}
}
func Serve(clientRequests chan *Request, wg *sync.WaitGroup) {
// 启动处理程序
for i := 0; i < MaxOutstanding; i++ {
wg.Add(1)
go handle(clientRequests, i, wg)
}
}
func main() {
clientRequests := make(chan *Request)
var wg sync.WaitGroup
go Serve(clientRequests, &wg)
for i := int32(0); i < 50; i++ {
clientRequests <- &Request{i}
}
close(clientRequests)
wg.Wait()
}
请注意,在 playground 中,该示例似乎偏向于单个 goroutine 成为接收者。通常情况下,被阻塞的 goroutine 会随机接收数据,如果你编译并正常运行,就可以看到这种行为。
英文:
You're blocking the iteration over the channel in your handle function with the send on the done
channel, because nothing is receiving on the other side.
Those extra channels aren't really doing anything, and you could just add a WaitGroup
to synchronize the handler's exit, then you can remove the done
channel which will allow the handler to continue.
func handle(queue chan *Request, i int, wg *sync.WaitGroup) {
defer wg.Done()
for r := range queue {
fmt.Println(i, "---", r)
}
}
func Serve(clientRequests chan *Request, wg *sync.WaitGroup) {
// Start handlers
for i := 0; i < MaxOutstanding; i++ {
wg.Add(1)
go handle(clientRequests, i, wg)
}
}
func main() {
clientRequests := make(chan *Request)
var wg sync.WaitGroup
go Serve(clientRequests, &wg)
for i := int32(0); i < 50; i++ {
clientRequests <- &Request{i}
}
close(clientRequests)
wg.Wait()
}
https://play.golang.org/p/oUFjZONjhk (note that in the playground, this example seems to currently favor a single goroutine being the receiver. Normally the blocked goroutines will receive randomly, and you can see that behavior if you compile and run normally)
答案2
得分: 1
在for循环中,你只处理了第5个元素的通道操作,然而在主函数中,你试图向通道发送值,但该通道已关闭。
为了解决这个问题,你可以在for循环中发送请求值:
for i := 0; i < MaxOutstanding; i++ {
clientRequests <- &Request{int32(i)}
}
以下是工作的代码:
package main
import (
"fmt"
)
type Request struct {
int32
}
var MaxOutstanding = 10
func handle(queue chan *Request, i int, done chan bool) {
for r := range queue {
fmt.Println(i, "---", r)
done <- true
}
}
func Serve(clientRequests chan *Request, quit, done chan bool) {
// Start handlers
for i := 0; i < MaxOutstanding; i++ {
go handle(clientRequests, i, done)
}
<-quit // Wait to be told to exit.
}
func main() {
clientRequests := make(chan *Request)
quit := make(chan bool)
done := make(chan bool)
go Serve(clientRequests, quit, done)
for i := 0; i < MaxOutstanding; i++ {
clientRequests <- &Request{int32(i)}
}
fmt.Println("...........>", <-done)
close(clientRequests)
close(done)
}
你可以在这里查看代码:https://play.golang.org/p/L5Y2YoFNvz
英文:
Inside the for loop you are handling the channel operation only to the 5th element, however in the main function you are trying to send over the value to the channel, which is closed.
To overcome this situation you can send the request value inside a for loop:
for i := 0; i < MaxOutstanding; i++ {
clientRequests <- &Request{int32(i)}
}
Here is the working code:
package main
import (
"fmt"
)
type Request struct {
int32
}
var MaxOutstanding = 10
func handle(queue chan *Request, i int, done chan bool) {
for r := range queue {
fmt.Println(i, "---", r)
done <- true
}
}
func Serve(clientRequests chan *Request, quit, done chan bool) {
// Start handlers
for i := 0; i < MaxOutstanding; i++ {
go handle(clientRequests, i, done)
}
<-quit // Wait to be told to exit.
}
func main() {
clientRequests := make(chan *Request)
quit := make(chan bool)
done := make(chan bool)
go Serve(clientRequests, quit, done)
for i := 0; i < MaxOutstanding; i++ {
clientRequests <- &Request{int32(i)}
}
fmt.Println("...........>", <-done)
close(clientRequests)
close(done)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论