【问题标题】:How can I spawn go routines that depend on their predecessors?我如何生成依赖于其前辈的 goroutine?
【发布时间】:2015-12-16 23:17:20
【问题描述】:

比如说我想填充这个矩阵:

| 0 | 0 | 0 | 0 |
| 0 | 1 | 2 | 3 |
| 0 | 2 | 3 | 4 |
| 0 | 3 | 4 | 5 |

具体来说,我想填充它,以便每个单元格都遵循规则,

在英语中,一个单元格的值比其toplefttopleft 邻居值的最大值大一。

因为每个单元格只有三个依赖项(它的 toplefttopleft 邻居),我可以将单元格填充到 m[1][1]1),一旦填充完成,我就可以填充标记为 2 的单元格,因为它们的所有依赖项都已填充。

| 0 | 0 | 0 | 0 |          | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 0 |   ---\   | 0 | 1 | 2 | 0 |
| 0 | 0 | 0 | 0 |   ---/   | 0 | 2 | 0 | 0 |
| 0 | 0 | 0 | 0 |          | 0 | 0 | 0 | 0 |

如何启动 1 个 goroutine,然后是 2,然后是 3,然后是 4...,以填充此矩阵的每个对角线?也许更具体地说,我如何才能在开始依赖单元之前等待邻居完成?

[编辑]:感谢@Zippoxer 的评论!为了澄清,我问的是 Go 中运行一个依赖于另一个先完成的 go-routine 的语法是什么。因为只有一个 go-routine 完成后才能启动多个新的 go-routine,所以它并不像没有并发地调用那么简单!

【问题讨论】:

  • 我认为这与数学和算法有关,而不是 goroutines(提示:尝试math.stackexchange.com)。如果您必须等待邻居完成才能计算更多单元格,那么并发计算对您有何帮助?
  • 这是个好问题。因此,对于第一个单元格,使其并发是没有意义的。但是在填充1 之后,您现在可以运行两个 单元,而彼此之间没有任何依赖关系。然后是 3,以此类推。对于大型矩阵(n > # of cores),这开始变得更有意义。
  • 这很有趣。随着矩阵深入右下角,我仍然很难弄清楚它如何同时工作:/
  • 我回答了,如果您需要更复杂的等待机制,请详细说明。

标签: go concurrency goroutine


【解决方案1】:

希望这是一个人为的例子,因为这绝对不是最快的解决方案,但也许它就是你想要的。

每个单元都运行在自己的 goroutine 中,并且有自己的通道,大致代表了它的依赖关系。一旦从其通道中读取三个值,单元就知道其依赖关系已全部解决。当一个单元完成时,它会将一些值传递给其所有依赖项的通道。

import "sync"

type empty struct{}

func contrivedMathThing(i, j int) ([][]int) {

    var wg sync.WaitGroup
    wg.Add(i * j)

    // Make two-dimensional slices for the channels that the goroutines will
    // wait on, and for the results of each cell. Two dimensional slices are
    // more annoying to make, but I think make the example a bit more clear.
    chans := make([][]chan empty, i)
    results := make([][]int, i)
    for a := 0; a < i; a++ {
        chans[a] = make([]chan empty, j)
        results[a] = make([]int, j)
        for b := 0; b < j; b++ {
            chans[a][b] = make(chan empty, 3)
        }
    }

    for a := 0; a < i; a++ {
        for b := 0; b < j; b++ {
            go func(a, b int, waitc <-chan empty) {
                defer wg.Done()

                // Wait for all dependencies to complete
                <-waitc
                <-waitc
                <-waitc

                // Compute the result
                // Too lazy to write...

                // Save the result to the results array
                results[a][b] = result

                // Signal all dependents that one of their dependencies has
                // resolved
                if a < i - 1 {
                    chans[a + 1][b] <- empty{} 
                }
                if b < j - 1 {
                    chans[a][b + 1] <- empty{}
                }
                if a < i - 1 && b < j - 1 {
                    chans[a + 1][b + 1] <- empty{}
                }

            }(a, b, chans[a][b])
        }
    }

    // All cells in the first row and first column need to start out with two
    // of their dependencies satisfied.
    for a := 1; a < i; a++ {
        chans[a][0] <- empty{}
        chans[a][0] <- empty{}
    }
    for b := 1; b < j; b++ {
        chans[0][b] <- empty{}
        chans[0][b] <- empty{}
    }

    // Unblock the cell at [0][0] so this show can get started
    close(chans[0][0])

    wg.Wait()

    return results
}

【讨论】:

  • 使用通道中的出局数作为解锁工具——我喜欢这样!这是常见的做法吗? (无论如何,它非常聪明。)
  • 是的,如果它出现的话,我会说这是规范的方式。你也可以使用 sync/atomic 做类似的事情。
【解决方案2】:

使用channels

一个 goroutine 等待另一个 goroutine:

done := make(chan bool)

go func() {
  // Work...
  done <- true // Signal we're done.
}()

go func() {
  <- done // Wait for the previous goroutine to signal.
  // Work...
}()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-31
    • 2015-12-23
    • 1970-01-01
    • 1970-01-01
    • 2012-10-29
    • 1970-01-01
    • 2013-03-08
    相关资源
    最近更新 更多