【问题标题】:all goroutines are asleep - deadlock所有 goroutine 都处于休眠状态 - 死锁
【发布时间】:2013-11-22 10:49:01
【问题描述】:

对于我的要求之一,我必须创建 N 个工人 go 例程,这些例程将由一个监控例程监控。当所有工作程序完成时,监控程序必须结束。我的代码以死锁结尾,请帮忙。

import "fmt"
import "sync"
import "strconv"

func worker(wg *sync.WaitGroup, cs chan string, i int ){
    defer wg.Done()
    cs<-"worker"+strconv.Itoa(i)    
}

func monitorWorker(wg *sync.WaitGroup, cs chan string) {
    defer wg.Done()
    for i:= range cs {
            fmt.Println(i)
     }
}
func main() {
    wg := &sync.WaitGroup{}
    cs := make(chan string)

    for i:=0;i<10;i++{
             wg.Add(1)
             go worker(wg,cs,i)
    } 

    wg.Add(1)
    go monitorWorker(wg,cs)
    wg.Wait()
}

【问题讨论】:

    标签: go deadlock channel


    【解决方案1】:

    你的 monitorWorker 永远不会死。当所有工作人员完成后,它会继续等待 cs。这种死锁是因为在 cs 上不会发送任何其他内容,因此 wg 永远不会达到 0。一个可能的解决方法是让监视器在所有工作人员完成后关闭通道。如果for循环在main中,则结束循环,从main返回,结束程序。

    例如:http://play.golang.org/p/nai7XtTMfr

    package main
    
    import (
        "fmt"
        "strconv"
        "sync"
    )
    
    func worker(wg *sync.WaitGroup, cs chan string, i int) {
        defer wg.Done()
        cs <- "worker" + strconv.Itoa(i)
    }
    
    func monitorWorker(wg *sync.WaitGroup, cs chan string) {
        wg.Wait()
        close(cs)
    }
    
    func main() {
        wg := &sync.WaitGroup{}
        cs := make(chan string)
    
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go worker(wg, cs, i)
        }
    
        go monitorWorker(wg, cs)
    
        for i := range cs {
            fmt.Println(i)
    
        }
    }
    

    编辑:这是对 OP 第一条评论的回答。

    您的程序有三个部分需要同步。首先,您的所有工作人员都需要发送数据。然后您的打印循环需要打印该数据。然后你的主函数需要返回从而结束程序。在您的示例中,所有工作人员都发送数据,所有数据都被打印,但消息永远不会发送到 main ,它应该正常返回。

    在我的示例中,main 负责打印,而“monitorWorker”只是告诉 main 何时收到需要打​​印的每条数据。这样程序就会优雅地结束,而不是死锁。

    如果你坚持打印循环在另一个 goroutine 中,你可以这样做。但是随后需要向 main 发送额外的通信,以便它返回。在下一个示例中,我使用通道来确保打印所有数据时主端。

    package main
    
    import (
        "fmt"
        "strconv"
        "sync"
    )
    
    func worker(wg *sync.WaitGroup, cs chan string, i int) {
        defer wg.Done()
        cs <- "worker" + strconv.Itoa(i)
    }
    
    func monitorWorker(wg *sync.WaitGroup, cs chan string) {
        wg.Wait()
        close(cs)
    }
    
    func printWorker(cs <-chan string, done chan<- bool) {
        for i := range cs {
            fmt.Println(i)
        }
    
        done <- true
    }
    
    func main() {
        wg := &sync.WaitGroup{}
        cs := make(chan string)
    
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go worker(wg, cs, i)
        }
    
        go monitorWorker(wg, cs)
    
        done := make(chan bool, 1)
        go printWorker(cs, done)
        <-done
    }
    

    【讨论】:

    • 感谢您的帮助。但这并不能解决我的并行处理问题。在我的情况下,monitorWorker 必须并行工作。(即从通道打印)。
    • @vrbilgi,我在答案中发表了评论。
    • 我尝试了不同的变体,但你的答案很简单优雅。thk:D
    【解决方案2】:

    如果您知道频道收到的消息数量,那么您可以限制您的循环;

    //c is channel
    for a := 1; a <= 3; a++{
            fmt.Println(<-c)
    }
    

    您还可以将另一个通道(worker 的状态)传递给 worker,然后有条件地停止导致死锁的循环。

    Ps:这只是一个额外的快速解决方案。没有专门针对您的解决方案。

    【讨论】:

      猜你喜欢
      • 2012-09-06
      • 2021-04-05
      • 1970-01-01
      • 2015-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-11
      • 1970-01-01
      相关资源
      最近更新 更多