【问题标题】:all goroutines are asleep - deadlock! with waitgroup所有的 goroutine 都在休眠 - 死锁!与等待组
【发布时间】:2019-01-24 20:53:22
【问题描述】:

这是我的代码,我哪里出错了?

func main() {
  intChan := make(chan int)
  wg := sync.WaitGroup{}

  for i := 0;i<5;i++{
    wg.Add(1)
    go send(intChan,i,&wg)
  }

  wg.Add(1)
  go get(intChan,&wg)
  wg.Wait()
  time.Sleep(5*time.Second)
  close(intChan)
}

func send(c chan int,index int,wg *sync.WaitGroup){
  defer func() {
    wg.Done()
  }()

  c <- index
}

func get(c chan int,wg *sync.WaitGroup){
  defer func() {
    wg.Done()
  }()

  for i := range c{
    fmt.Printf("%d\n",i)
  }
}

当我运行它时,我收到错误fatal error: all goroutines are asleep - deadlock!

这是错误信息:

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc0000120d8)
    C:/Go/src/runtime/sema.go:56 +0x40
sync.(*WaitGroup).Wait(0xc0000120d0)
    C:/Go/src/sync/waitgroup.go:130 +0x6b
main.main()
    F:/go/src/demo/channel.go:94 +0xf9

goroutine 10 [chan receive]:
main.get(0xc00001c120, 0xc0000120d0)
    F:/go/src/demo/channel.go:112 +0xe0
created by main.main
    F:/go/src/demo/channel.go:92 +0xeb

谢谢大家,这是我的第一个问题。

【问题讨论】:

  • get 在通道关闭之前不会返回,并且您的主 goroutine 在get 返回之前不会关闭通道(因为那是它执行wg.Done() 的时间)。那是你的僵局。
  • 你还可以在循环中捕获 i ,每次 send() 运行时,它的值可以是 0 到 4 之间的任何值。
  • 忽略以上评论。发送的调用正确捕获了 i。
  • @AndySchweig 我该如何解决?我无法修复它。tks

标签: go


【解决方案1】:

正如 Andy 在 cmets 中所说,只有在收到所有输入并关闭通道时,您才会退出 get 函数。如您所知,有五样东西要接收,您可以在发送中使用类似的for 循环:

func main() {
    intChan := make(chan int)
    wg := sync.WaitGroup{}

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go send(intChan, i, &wg)
    }

    wg.Add(1)
    go get(intChan, &wg)
    wg.Wait()
    close(intChan)
}

func send(c chan int, index int, wg *sync.WaitGroup) {
    defer func() {
        wg.Done()
    }()

    c <- index
}

func get(c chan int, wg *sync.WaitGroup) {
    defer func() {
        wg.Done()
    }()

    for i := 0; i < 5; i++ {
        input := <- c
        fmt.Printf("%d\n", input)
    }
}

https://play.golang.org/p/CB8HUKPBu2I

如果您想坚持在频道上进行测距,那么您必须在发送完所有消息后关闭它,我会通过添加第二个等待组来做到这一点:

func main() {
    intChan := make(chan int)
    allSent := sync.WaitGroup{}

    for i := 0; i < 5; i++ {
        allSent.Add(1)
        go send(intChan, i, &allSent)
    }

    allReceived := sync.WaitGroup{}
    allReceived.Add(1)
    go get(intChan, &allReceived)

    allSent.Wait()
    close(intChan)
    allReceived.Wait()
}

func send(c chan int, index int, wg *sync.WaitGroup) {
    defer func() {
        wg.Done()
    }()

    c <- index
}

func get(c chan int, wg *sync.WaitGroup) {
    defer func() {
        wg.Done()
    }()

    for i := range c {
        fmt.Printf("%d\n", i)
    }
}

https://play.golang.org/p/svFVrBdwmAc

【讨论】:

    【解决方案2】:

    这可以工作!

    func main() {
        intChan := make(chan int)
        wg := sync.WaitGroup{}
    
        for i := 0;i<5;i++{
            wg.Add(1)
            go send(intChan,i,&wg)
        }
    
        wg.Add(1)
        go get(intChan,&wg)
    
        wg.Wait()
        close(intChan)
    }
    
    func send(c chan int,index int,wg *sync.WaitGroup){
        defer func() {
            wg.Done()
        }()
    
        c <- index
    }
    
    func get(c chan int,wg *sync.WaitGroup){
        defer func() {
            wg.Done()
        }()
    
        for {
            select {
                case i := <-c:
                    fmt.Printf("%d\n",i)
                default:
                    return
            }
        }
    }
    

    【讨论】:

    • 不,它不能:play.golang.org/p/7ix3rRU4_OT 问题是,当 c 通道中没有任何内容时,将触发 select 中的默认情况,因为它是阻塞通道,可能会在第一次接收已经发生。然后 get 将在第一次接收后退出,因为通道中没有添加任何新内容,并且您再次陷入僵局。
    猜你喜欢
    • 1970-01-01
    • 2014-02-22
    • 1970-01-01
    • 2021-06-30
    • 1970-01-01
    • 1970-01-01
    • 2013-11-22
    • 2018-05-28
    相关资源
    最近更新 更多