【问题标题】:goroutine asleep - deadlockgoroutine 睡着了 - 死锁
【发布时间】:2017-03-18 08:00:59
【问题描述】:

在下面的代码中,我试图生成MaxOutstanding 数量的处理程序。每个处理程序循环遍历队列queue 中的项目并将它们打印出来,我还将true 写入done 通道。

在我的主函数中,我启动处理程序并将 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)
}

执行时出现以下错误。我看不出执行有什么问题,我什至关闭了频道。

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("....", &lt;- done) 不足以表明done 频道有消费者。我将代码按执行顺序向上移动。当数据写入通道时,消费者需要在通道上“监听”。在我之前的代码中,写入第一个数据时没有消费者。

工作代码。

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

【问题讨论】:

  • 乍一看,你给done写了至少5次,但只读了一次。
  • @myaut ,这就是意图。我只对写入完成的数据的第一个实例感兴趣。

标签: go deadlock goroutine


【解决方案1】:

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

【讨论】:

  • 感谢 WaitGroup 的回答。我认为这是同步多个 goroutine 的更好解决方案。
  • "因为另一边没有接收任何东西" 根据代码,有 1 行实际上是在另一边接收数据fmt.Println( "...........&gt;", &lt;- done )。我按执行顺序向上移动了队列,它运行良好。我想频道需要在写入数据时有消费者,而不是在未来的某个时候。
  • @Pratyush:是的,有 1 次接收,但您需要连续接收以保持这些 for 循环继续进行。
【解决方案2】:

在 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

【讨论】:

猜你喜欢
  • 2020-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-06
  • 1970-01-01
  • 2012-01-06
  • 2013-10-04
  • 1970-01-01
相关资源
最近更新 更多