需求

fatal error: all goroutines are asleep - deadlock!

代码

package main

import (
    "fmt"
    "log"
    "math/rand"
    "sync"
    "time"
)

type Row struct {
    Id int `json:"id"`
}

type Report struct {
    Name string `json:"name"`
}

type Bag struct {
    Reports []Report `json:"reports"`
    I       int      `json:"i"`
    J       int      `json:"j"`
}

func GetRows() (rows []Row) {

    rows = make([]Row, 0)
    for i := 0; i < rand.Intn(10); i++ {
        rows = append(rows, Row{Id: i})
    }

    return
}

func GetReports(m, n int) (reports []Report) {
    reports = make([]Report, 0)
    for i := 0; i < rand.Intn(100); i++ {
        reports = append(reports, Report{Name: fmt.Sprintf("r: in %d rows[%d] report %d", m, n, i)})
    }
    return
}

func main() {

    var (
        cLimit = 3 // 线程限制
        wg     = sync.WaitGroup{}
    )

    c := make(chan int, cLimit)
    c2 := make(chan int, 10)
    cReport := make(chan Bag, 100)
    defer close(c)
    defer close(cReport)

    go func() {
        log.Println("in Rec Start")
        defer func() {
            log.Println("in Rec Finish")
        }()
        //Rec:
        for {
            select {
            case r, ok := <-cReport:
                if !ok {
                    c2 <- 1
                }
                log.Printf("rec %v from %d rows[%d] len=%d\n ", ok, r.I, r.J, len(r.Reports))
            }
        }
    }()

    for i := 0; i < 10; i++ {
        rows := GetRows()
        wg.Add(1)
        c <- 1
        log.Printf("add %d rows=%d", i, len(rows))
        go func(rows []Row, i int) {
            log.Println("in ", i)
            for j := 0; j < len(rows); j++ {
                rs := GetReports(i, j)
                log.Printf("in %d rows[%d] get rs:%d to send\n", i, j, len(rs))
                cReport <- Bag{Reports: rs, I: i, J: j}
                time.Sleep(time.Second * time.Duration(rand.Intn(7)))
            }
            defer func() {
                wg.Done()
                log.Println("leave ", i)
            }()
            <-c
        }(rows, i)

        if i%cLimit == 0 {
            fmt.Println()
        }
    }
    log.Println("after loop")
Rec:
    for {
        select {
        case r, ok := <-cReport:
            if !ok {
                break Rec
            }
            log.Printf("rec %v from %d rows[%d] len=%d\n ", ok, r.I, r.J, len(r.Reports))
        }
    }
    log.Println("after work")
    wg.Wait()
    select {
    case v, ok := <-c2:
        fmt.Println("v,ok = ", v, ok)
        break
    }
    log.Println("Finish")
}

里面开了2个通道 c 和 cReport,c是为了限制协程的数量,使用了wg来进行限制线程数量。
cReport 是报告的结果的通道,但现在问题是,如果加了 c 来限制协程数量,cReport 就会报错deadlock 死锁。

如何实现,既可以将数据从 协程中接收,又可以限制协程数量?