doushan2811 2019-02-11 05:30
浏览 70
已采纳

我应该在哪个函数中传递WaitGroup?

I have made a simple code example to understand the usage of pipeline, here it is.

package main

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

func main() {
    ch1 := make(chan int, 10) // Use buffered channel so as to avoid clogging
    ch2 := make(chan string, 10)
    var wg sync.WaitGroup
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func1(i, ch1, &wg)
        go func2(ch1, ch2)
    }
    wg.Wait()
    close(ch1)
    for val := range ch2 {
        fmt.Println(val)
    }
}

func func1(seconds int, ch chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    time.Sleep(time.Duration(seconds) * time.Second)
    fmt.Println(seconds)
    ch <- seconds
}

func func2(ch1 chan int, ch2 chan string) {
    for range ch1 {
        ch2 <- "hello"
    }
    close(ch2)
}

Now, the problem is I don't get consistent output ( I understand it's some concurrency issue, which I haven't fully understood ).

Output

> go run pipeline-loop.go 
0
1
2
hello
hello

> go run pipeline-loop.go 
0
1
2
hello
hello
hello

> go run pipeline-loop.go 
0
1
2
hello
hello

> go run pipeline-loop.go 
0
1
2
hello
hello

> go run pipeline-loop.go 
0
1
2
hello
hello
panic: close of closed channel

goroutine 6 [running]:
main.func2(0xc00006c000, 0xc000056180)
    /home/projects/go-tuts/pipeline-loop.go:36 +0x72
created by main.main
    /home/projects/go-tuts/pipeline-loop.go:16 +0x10f
exit status 2

Another guy changed the code ( and it was working ) and put func2 outside the loop but I want func2 for each iteration of the func1.

Problem

So, I want to understand as to where should the WaitGroup and close(ch) be used ?

Thanks.
Temporarya
( A golang noobie )

Update

Based on a user's answer, I changed the code, now I get output as expected ( but not the solution to this question ) but still there's a deadlock. https://play.golang.org/p/O_rp_FLvNh8

package main

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

func main() {
    ch1 := make(chan int, 10) // Use buffered channel so as to avoid clogging
    ch2 := make(chan string, 10)
    var wg1 sync.WaitGroup
    // var wg2 sync.WaitGroup
    for i := 0; i < 3; i++ {
        wg1.Add(1)
        go func1(i, ch1)
        go func2(ch1, ch2, &wg1)
    }
    for val := range ch2 {
        fmt.Println(val)
    }
    wg1.Wait()
    close(ch1)
    close(ch2)
}

// func func1(seconds int, ch chan<- int, wg *sync.WaitGroup) {
func func1(seconds int, ch chan<- int) {
    // defer wg.Done()
    time.Sleep(time.Duration(seconds) * time.Second)
    fmt.Println(seconds)
    ch <- seconds
}

func func2(ch1 chan int, ch2 chan string, wg *sync.WaitGroup) {
    defer wg.Done()
    for range ch1 {
        ch2 <- "hello"
    }
}
  • 写回答

2条回答 默认 最新

  • doudu6100 2019-02-11 05:44
    关注

    There are multiple problems in your code.

    In the loop, you are spawning multiple (3) goroutines that runs func2, and in func2, you send data to ch2 and call close(ch2). This is a problem. It can happen that when one goroutine is seding data to ch2, the other has closed that channel, which causes:

    panic: close of closed channel
    
    goroutine 6 [running]:
    main.func2(0xc00006c000, 0xc000056180)
        /home/projects/go-tuts/pipeline-loop.go:36 +0x72
    created by main.main
        /home/projects/go-tuts/pipeline-loop.go:16 +0x10f
    exit status 2
    

    In general, you don't need close a chanel multiple times - you only need to shut them once they are all finished. You need another WaitGroup for this; you need to pass both functions a WaitGroup.

    Further reading: https://blog.golang.org/pipelines

    UPDATE:

    Personally I use a pattern for "works" that produce data into a same channel and the channel needs to be closed after all works are done:

    for something {
        wg.Add(1)
        go func(i int) {
            work(ch)
            wg.Done()
        }
    }
    
    go func() {
        wg.Wait()
        close()
    }()
    

    I think it is a good idea that keeps the API clean from WorkGroup as WorkGroup is about how you sync the work instead of how the work is done.

    I have changed your code into this pattern: https://play.golang.org/p/vdCNsxWhgyQ

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 Python安装cvxpy库出问题
  • ¥15 用前端向数据库插入数据,通过debug发现数据能走到后端,但是放行之后就会提示错误
  • ¥15 python天天向上类似问题,但没有清零
  • ¥30 3天&7天&&15天&销量如何统计同一行
  • ¥30 帮我写一段可以读取LD2450数据并计算距离的Arduino代码
  • ¥15 C#调用python代码(python带有库)
  • ¥15 矩阵加法的规则是两个矩阵中对应位置的数的绝对值进行加和
  • ¥15 活动选择题。最多可以参加几个项目?
  • ¥15 飞机曲面部件如机翼,壁板等具体的孔位模型
  • ¥15 vs2019中数据导出问题