【问题标题】:Stuck with Channels in golang卡在 golang 中的频道
【发布时间】:2017-10-06 09:36:38
【问题描述】:

我需要并行运行一个函数。
如果即使函数返回true(在频道上发送true),那么最终结果应该是true

如何使用 goroutine 和通道实现这一点?

// Some performance intensive function
func foo(i int, c chan bool) {
    // do some processing and return either true or false
    c <- true // or false
}

func main() {
    flg := false
    ch := make(chan bool)
    for i := 0; i < 10; i++ {
        go foo(i, ch)
    }
    // If even once foo() returned true then val should be true
    flg = flg || <-ch
}

【问题讨论】:

  • "如何使用渠道实现这一目标?" --- 有什么理由必须为此使用频道?
  • 请向我们展示您的尝试。您的代码根本不使用频道。
  • (另外,建议您使用gofmt,因为您的代码不易阅读)
  • @zerkms 频道不是必需的。只是最初对 foo() 的调用是连续的。现在我想并行运行它们。我认为只有 goroutine 无济于事,因为我也需要返回值。欢迎任何其他想法。提前致谢。
  • 请在询问之前阅读频道并将相关代码放在这里。 Golang 之旅是一种资源tour.golang.org/concurrency/2

标签: go channel goroutine


【解决方案1】:

您可以从频道ch 开始阅读,并在获得真实结果后将flg 设置为true。像这样:

//flg = flg || <- ch
for res := range ch {
    if res {
        flg = true
    }
}

这种方法有效但有一个严重的缺点 - for 循环无限等待来自通道的新值。停止循环的惯用方法是关闭通道。你可以这样做:运行一个单独的 goroutine,它会等到所有 goroutine 退出。 Go 提供了一个非常方便的工具来做到这一点 - sync.WaitGroup

在全局范围内定义它,以便每个 goroutine 都可以访问它:

var (
    wg sync.WaitGroup
)

然后,每次启动 goroutine 时,都会向等待组添加一个 goroutine:

for i := 0; i < 10; i++ {
    wg.Add(1)   // here
    go foo(i, ch)
}

当 goroutine 完成时,它会调用 wg.Done 方法来标记它。

func foo(i int, c chan bool) {
    //do some processing and return either true or false
    c <- true   //or false
    wg.Done()   // here
}

然后 sepatate goroutine 等待,直到所有 foo goroutine 退出并关闭通道。 wg.Wait 阻塞直到全部完成:

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

大家一起:https://play.golang.org/p/8qiuA29-jv

【讨论】:

    【解决方案2】:

    您只从通道接收一个值(这将是由foo() 调用之一发送的值,无法预测其中的哪一个),但您想接收全部。

    所以使用for 循环来接收与您发送(发送)的值一样多的值:

    for i := 0; i < 10; i++ {
        flg = flg || <-ch
    }
    

    尽管在您的情况下,循环直到收到一个 true 值就足够了,因为这将确定 flg 的最终值,但仍然建议接收所有值,否则剩余的 goroutine 将被阻止(因为ch 是一个无缓冲通道)。在这个例子中,这无关紧要,但在“现实生活”应用程序中,它会导致 goroutines 永远卡住(内存泄漏)。

    如果您不想等待所有foo() 调用完成并尽快返回(只要遇到一个true 值),一个选项是使ch 缓冲,所以所有goroutine 可以在其上发送值而不会被阻塞。这样您就不需要接收(并因此等待)所有 foo() 调用来完成:

    ch := make(chan bool, 10)
    for i := 0; i < 10; i++ {
        go foo(i, ch)
    }
    
    flg := false
    for i := 0; i < 10; i++ {
        if <-ch {
            flg = true
            break
        }
    }
    

    选择这种方法,您应该提供取消不再需要工作的 goroutine 的方法,以避免不必要的 CPU(和内存)使用。 context.Context 就是这样一个意思,在这里阅读更多关于它的信息:Close multiple goroutine if an error occurs in one in go

    【讨论】:

      猜你喜欢
      • 2018-10-12
      • 2019-02-02
      • 2018-04-26
      • 1970-01-01
      • 2018-02-02
      • 1970-01-01
      • 2017-06-11
      • 2018-03-15
      • 2016-09-21
      相关资源
      最近更新 更多