为什么所有的 goroutines 都在睡觉 - 僵局。识别瓶颈

Why all goroutines are asleep - deadlock. Identifying bottleneck

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]: 同步。(*等待组)。等待(0xc082004600) c:/go/src/sync/waitgroup.go:132 +0x170 main.main() E:/Go/go_projects/go/src/Test.go:22 +0x21a

我刚刚尝试了它 (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)
}

注意 &wg:您正在按值传递指向原始 sync.WaitGroup 的指针,供 goroutine 使用。

如前所述,不要按值传递同步包中的类型,就在 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