douyoufan7881 2016-05-12 21:51
浏览 36
已采纳

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

I'll use a hacky inefficient prime number finder to make this question a little more concrete.

Let's say our main function fires off a bunch of "worker" goroutines. They will report their results to a single channnel which prints them. But not every worker will report something so we can't use a counter to know when the last job is finished. Or is there a way?

For the concrete example, here, main fires off goroutines to check whether the values 2...1000 are prime (yeah I know it is inefficient).

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
}

My problem is that I don't know how to reliably stop it at the right time. I tried adding a sleep at the end of main and it works (but it might take too long, and this is no way to write concurrent code!). I would like to know if there was a way to send a stop signal through a channel or something so main can stop at the right time.

The trick here is that I don't know how many worker responses there will be.

Is this impossible or is there a cool trick?

(If there's an answer for this prime example, great. I can probably generalize. Or maybe not. Maybe this is app specific?)

  • 写回答

1条回答 默认 最新

  • dongyu1918 2016-05-12 22:03
    关注

    Use a WaitGroup.

    The following code uses two WaitGroups. The main function uses wgTest to wait for print_if_prime functions to complete. Once they are done, it closes the channel to break the for loop in the printing goroutine. The main function uses wgPrint to wait for printing to complete.

    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

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 java在应用程序里获取不到扬声器设备
  • ¥15 echarts动画效果的问题,请帮我添加一个动画。不要机器人回答。
  • ¥60 许可证msc licensing软件报错显示已有相同版本软件,但是下一步显示无法读取日志目录。
  • ¥15 Attention is all you need 的代码运行
  • ¥15 一个服务器已经有一个系统了如果用usb再装一个系统,原来的系统会被覆盖掉吗
  • ¥15 使用esm_msa1_t12_100M_UR50S蛋白质语言模型进行零样本预测时,终端显示出了sequence handled的进度条,但是并不出结果就自动终止回到命令提示行了是怎么回事:
  • ¥15 前置放大电路与功率放大电路相连放大倍数出现问题
  • ¥80 部署运行web自动化项目
  • ¥15 腾讯云如何建立同一个项目中物模型之间的联系
  • ¥30 VMware 云桌面水印如何添加