玩转 go,我把这段代码放在一起:

package main

import "fmt"

const N = 10

func main() {
    ch := make(chan int, N)
    done := make(chan bool)

    for i := 0; i < N; i++ {
        go (func(n int, ch chan int, done chan bool) {
            for i := 0; i < N; i++ {
                ch <- n*N + i
            }
            done <- true
        })(i, ch, done)
    }

    numDone := 0
    for numDone < N {
        select {
        case i := <-ch:
            fmt.Println(i)
        case <-done:
            numDone++
        }
    }

    for {
        select {
        case i := <-ch:
            fmt.Println(i)
        default:
            return
        }
    }
}

基本上我有 N 个 channel 在做一些工作并在同一个 channel 上报告——我想知道所有 channel 什么时候完成。所以我有另一个 done channel ,每个工作 goroutine 都会发送一条消息(消息无关紧要),这导致 main 将该线程计数为完成。当计数达到 N 时,我们实际上就完成了。

这是“好”吗?有没有更惯用的方法来做到这一点?

编辑:为了澄清一点,我很怀疑,因为 done channel 似乎正在做一个 channel 关闭似乎是为了的工作,但当然我实际上不能在任何 goroutine 中关闭 channel ,因为所有例程共享相同 channel 。所以我使用 done 来模拟一个执行某种“缓冲关闭”的 channel 。

编辑2:原始代码并没有真正起作用,因为有时来自例程的 done 信号在 int 之前被读取,它只是放在 ch 上。需要一个“清理”循环。

最佳答案

这里有一个sync.WaitGroup的惯用用法供大家学习

( playground link )

package main

import (
    "fmt"
    "sync"
)

const N = 10

func main() {
    ch := make(chan int, N)
    var wg sync.WaitGroup
    for i := 0; i < N; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            for i := 0; i < N; i++ {
                ch <- n*N + i
            }
        }(i)
    }
    go func() {
        wg.Wait()
        close(ch)
    }()
    for i := range ch {
        fmt.Println(i)
    }
}

注意两个go例程定义中闭包的使用,注意第二个go语句等待所有例程完成,然后关闭 channel ,这样range就可以使用了。

关于go - 编写此代码的更好的惯用方式?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16020406/

10-16 20:54