首页 > 解决方案 > 将数据从不同的 goroutine 写入同一个通道可以正常工作,无需等待组

问题描述

在等待 wg.Wait() 后,使用带有 waitgroup 的多个 go 例程将数据写入同一通道时,出现异常说所有 go 例程都处于睡眠状态或 deedlock。

package main

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

var wg sync.WaitGroup

func CreateMultipleRoutines() {
    ch := make(chan int)

    for i := 0; i < 10; i++ { // creates 10 go routines and adds to waitgroup
        wg.Add(1)
        go func() {
            for j := 0; j < 10; j++ {
                ch <- j
            }
            wg.Done() // indication of go routine is done to main routine
        }()
    }

    fmt.Println(runtime.NumGoroutine())
    wg.Wait()           //wait for all go routines to complete
    close(ch)           // closing channel after completion of wait fo go routines
    for v := range ch { // range can be used since channel is closed
        fmt.Println(v)
    }
    fmt.Println("About to exit program ...")
}

当试图在没有等待组的情况下实现这一点时,我可以通过循环将数据推送到通道的确切次数来从通道读取数据,但我无法确定范围,因为当我们关闭通道时会出现恐慌。这是示例代码

package main

import (
    "fmt"
    "runtime"
)

func main() {
    ch := make(chan int)


    for i := 0; i < 10; i++ { // creates 10 go routines and adds to waitgroup
        go func(i int) {
            for j := 0; j < 10; j++ {
                ch <- j * i
            }
        }(i)
    }

    fmt.Println(runtime.NumGoroutine())

    for v := 0; v < 100; v++ {
        fmt.Println(<-ch)
    }
    fmt.Println("About to exit program ...")
}

我想了解为什么处于等待状态的等待组仍在等待,即使所有 go 例程都已发出信号 Done(),这反过来又使 go 例程的数量为零

标签: go

解决方案


我认为您的原始代码存在一些问题。

  1. 您正在关闭通道,然后再读取它。
  2. 由于您的频道是 1“大小”,因此您没有获得使用 10 个 goroutine 的优势。所以一个 goroutine 每次产生一个结果。

我的解决方案是生成一个新的 goroutine 来监控 10 个 goroutine 是否完成了它的工作。在那里你将使用你的WaitGroup.

然后代码将是:

package main

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

var wg sync.WaitGroup

func main() {
    ch := make(chan int, 10)

    for i := 0; i < 10; i++ { // creates 10 go routines and adds to waitgroup
        wg.Add(1)
        go func() {
            for j := 0; j < 10; j++ {
                ch <- j
            }
            wg.Done() // indication of go routine is done to main routine
        }()
    }

    go func(){
        wg.Wait()
        close(ch)
    }()

    fmt.Println(runtime.NumGoroutine())
    for v := range ch { // range can be used since channel is closed
        fmt.Println(v)
    }
    fmt.Println("About to exit program ...")
}

推荐阅读