【问题标题】:Why all goroutines are asleep - deadlock. Identifying bottleneck为什么所有的 goroutine 都处于休眠状态 - 死锁。识别瓶颈
【发布时间】:2015-04-25 19:04:46
【问题描述】:
package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

func main() {
    intInputChan := make(chan int, 50)
    var wg sync.WaitGroup
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go worker(intInputChan, wg)
    }
    for i := 1; i < 51; i++ {
        fmt.Printf("Inputs. %d \n", i)
        intInputChan <- i
    }
    close(intInputChan)
    wg.Wait()
    fmt.Println("Existing Main App... ")
    panic("---------------")
}

func worker(input chan int, wg sync.WaitGroup) {
    defer func() {
        fmt.Println("Executing defer..")
        wg.Done()
    }()

    for {
        select {
        case intVal, ok := <-input:
            time.Sleep(100 * time.Millisecond)
            if !ok {
                input = nil
                return
            }
            fmt.Printf("%d  %v\n", intVal, ok)

        default:
            runtime.Gosched()
        }
    }

}

抛出的错误是。

致命错误:所有 goroutine 都处于休眠状态 - 死锁!

goroutine 1 [semacquire]: sync.(*WaitGroup).Wait(0xc082004600) c:/go/src/sync/waitgroup.go:132 +0x170 main.main() E:/Go/go_projects/go/src/Test.go:22 +0x21a

【问题讨论】:

标签: go channel


【解决方案1】:

如前所述,不要通过值传递来自同步包的类型,就在sync package documentation 的顶部附近:“不应复制包含此包中定义的类型的值。 " 这也包括类型本身(sync.Mutexsync.WaitGroup 等)。

但是,有几点注意事项:

  • 如果您知道要添加多少个,您可以只调用一次wg.Add(但如文档所述,请确保在任何可以调用Wait 之前完成之前)。
  • 你不想这样打电话给runtime.Gosched;它使工作人员忙于循环。
  • 您可以使用range 从通道中读取数据,以简化关闭时的停止操作。
  • 对于小型函数,您可以使用闭包,而无需费心通过通道或等待组。

这就变成了这样:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    const numWorkers = 3

    c := make(chan int, 10)
    var wg sync.WaitGroup

    wg.Add(numWorkers)
    for i := 0; i < numWorkers; i++ {
        go func() {
            defer func() {
                fmt.Println("Executing defer…")
                wg.Done()
            }()

            for v := range c {
                fmt.Println("recv:", v)
                time.Sleep(100 * time.Millisecond)
            }
        }()
    }

    for i := 1; i < 51; i++ {
        fmt.Println("send:", i)
        c <- i
    }
    fmt.Println("closing…")
    close(c)

    fmt.Println("waiting…")
    wg.Wait()
    fmt.Println("Exiting Main App... ")
}

playground

【讨论】:

  • 感谢 VonC 和 Dave 将其更改为引用类型完成了这项工作。
  • 我喜欢你提到的所有笔记。您能否为第 3 点(从频道读取的范围)提供更多理论,或者指针也可以提供帮助。
【解决方案2】:

我刚刚尝试 (playground) 传递了一个 wg *sync.WaitGroup 并且它有效。

传递sync.WaitGroup 意味着传递sync.WaitGroup 的副本(按值传递):goroutine 将Done() 提及到一个不同 sync.WaitGroup

var wg sync.WaitGroup
for i := 0; i < 3; i++ {
    wg.Add(1)
    go worker(intInputChan, &wg)
}

注意&amp;wg:你是通过值传递指向原始sync.WaitGroup的指针,供goroutine使用。

【讨论】:

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