我们如何确定Go中“最后一个”工作进程/线程何时完成?

时间:2016-05-12 21:51:21

标签: go concurrency

我将使用一个hacky低效的素数查找器来使这个问题更具体。

假设我们的主要功能是激发了一堆“工人”goroutines。他们会将结果报告给一个印有它们的通道。但是不是每个工作人员都会报告,所以我们不能使用计数器来知道上一个工作何时完成。或者有办法吗?

对于具体的例子,在这里,main触发goroutines以检查值2 ... 1000是否为素数(是的,我知道它是低效的)。

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int)
    go func () {
        for {
            fmt.Print(" ", <- c)
        }
    }()
    for n := 2; n < 1000; n++ {
        go printIfPrime(n, c)
    }
    time.Sleep(2 * time.Second)   // <---- THIS FEELS WRONG
}

func printIfPrime(n int, channel chan int) {
    for d := 2; d * d <= n; d++ {
        if n % d == 0 {
            return
        }
    }
    channel <- n
}

我的问题是我不知道如何在合适的时间可靠地阻止它。我尝试在main结束时添加一个睡眠并且它可以工作(但可能需要很长时间,而且这无法编写并发代码!)。我想知道是否有办法通过频道发送停止信号,以便main可以在正确的时间停止。

这里的诀窍是我不知道会有多少工人回应。

这是不可能的还是有一个很酷的伎俩?

(如果这个主要例子的答案很好,那很好。我可以概括。或者可能没有。也许这是特定于应用程序的?)

1 个答案:

答案 0 :(得分:3)

使用WaitGroup

以下代码使用两个WaitGroup。主函数使用wgTest等待print_if_prime函数完成。一旦完成,它将关闭通道以打破打印goroutine中的for循环。主函数使用wgPrint等待打印完成。

package main

import (
  "fmt"
  "sync"
) 

func main() {
  c := make(chan int)
  var wgPrint, wgTest sync.WaitGroup

  wgPrint.Add(1)
  go func(wg *sync.WaitGroup) {
    defer wg.Done()
    for n := range c {
        fmt.Print(" ", n)
    }
  }(&wgPrint)

  for n := 2; n < 1000; n++ {
    wgTest.Add(1)
    go print_if_prime(&wgTest, n, c)
  }

  wgTest.Wait()
  close(c)
  wgPrint.Wait()
}

func print_if_prime(wg *sync.WaitGroup, n int, channel chan int) {
  defer wg.Done()
  for d := 2; d*d <= n; d++ {
    if n%d == 0 {
        return
    }
  }
  channel <- n
}

playground example