为什么所有goroutine都在睡觉-死锁。 识别瓶颈

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 
", 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
", intVal, ok)

        default:
            runtime.Gosched()
        }
    }

}

error thrown is.

fatal error: all goroutines are asleep - deadlock!

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

I just tried it (playground) passing a wg *sync.WaitGroup and it works.

Passing sync.WaitGroup means passing a copy of the sync.WaitGroup (passing by value): the goroutine mentions Done() to a different sync.WaitGroup.

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

Note the &wg: you are passing by value the pointer to the original sync.WaitGroup, for the goroutine to use.

As mentioned, don't pass types from the sync package around by value, right near the top of the sync package documentation: "Values containing the types defined in this package should not be copied." That also includes the types themselves (sync.Mutex, sync.WaitGroup, etc).

However, several notes:

  • You can use just a single call to wg.Add if you know how many you're going to add (but as documented make sure it's done before anything can call Wait).
  • You don't want to call runtime.Gosched like that; it makes the workers busy loop.
  • You can use range to read from the channel to simplify stopping when it's closed.
  • For small functions you can use a closure and not bother to pass the channel or wait group at all.

That turns it into this:

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