goroutine 睡着了 - 死锁

标签 go deadlock goroutine

在下面的代码中,我试图生成 MaxOutstanding处理程序的数量。每个处理程序循环遍历队列中的项目 queue并打印出来,我也写了truedone channel 。

在我的主函数中,我启动处理程序并将 9 个元素写入 queue并等待第一个元素写入 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)
}

执行时出现以下错误。我看不出实现有什么问题,我什至要关闭 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

编辑:

显然,fmt.Println("....", <- done)不足以表示 done 有消费者 channel 。我在执行顺序中向上移动了代码。当数据写入时,消费者需要在 channel 上“收听”。在我之前的代码中,写入第一个数据时没有消费者。

工作代码。

https://play.golang.org/p/98l2M4XO9t

最佳答案

您正在通过 done channel 上的发送阻止 handle 函数中 channel 的迭代,因为另一端没有接收任何东西。

那些额外的 channel 并没有真正做任何事情,你可以只添加一个 WaitGroup 来同步处理程序的退出,然后你可以删除 done channel ,这将允许处理程序继续。

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 (请注意,在 playground 中,此示例目前似乎倾向于使用单个 goroutine 作为接收方。通常被阻塞的 goroutine 会随机接收,如果您正常编译和运行,您可以看到该行为)

关于goroutine 睡着了 - 死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40423461/

相关文章:

if-statement - go - 如何使用golang goroutine,select和if语句返回?

go - 在完成时将 channel 结果添加到队列的更惯用的方法

go - 在golang regex中取消整个字符串

go - 如何在golang中发送一个假的udp包

java - 无法使用下一个代码重现死锁

sql - 从不断插入的表中选择

string - Go 二进制数据在字符串转换时被截断

go - 如何在 go.mod 中最好地声明 golang 依赖版本?

c - 有人有非异步安全信号处理程序死锁的示例吗

go - 为什么用goroutines for循环会导致数据丢失