【问题标题】:Goroutines not exiting when data channel is closed数据通道关闭时 Goroutines 不退出
【发布时间】:2014-08-27 06:29:07
【问题描述】:

我正在尝试遵循http://blog.golang.org/pipelines/bounded.go 上发布的有界 goroutine 示例。我遇到的问题是,如果增加的工人数量超过了要做的工作量,那么额外的工人永远不会被取消。其他一切似乎都正常工作,值被计算和记录,但是当我关闭groups 频道时,工作人员只是挂在范围语句上。

我想我不明白(在我的代码和示例代码中)是工人如何知道没有更多工作要做并且他们应该退出?

更新

一个工作(即非工作)示例发布在http://play.golang.org/p/T7zBCYLECp。它显示了工人的僵局,因为他们都睡着了,没有工作要做。我感到困惑的是,我认为示例代码会有同样的问题。

这是我目前正在使用的代码:

// Creates a pool of workers to do a bunch of computations
func computeAll() error {
    done := make(chan struct{})
    defer close(done)

    groups, errc := findGroups(done)

    // start a fixed number of goroutines to schedule with
    const numComputers = 20     
    c := make(chan result)
    var wg sync.WaitGroup
    wg.Add(numComputers)
    for i := 0; i < numComputers; i++ {
        go func() {
            compute(done, groups, c)
            wg.Done()
        }()
    }

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

    // log the results of the computation
    for r := range c { // log the results }

    if err := <-errc; err != nil {
        return err
    }

    return nil
}

这是用数据填充通道的代码:

// Retrieves the groups of data the must be computed
func findGroups(done <-chan struct{}) (<-chan model, <-chan error) {
    groups := make(chan model)
    errc := make(chan error, 1)
    go func() {
        // close the groups channel after find returns
        defer close(groups)

        group, err := //... code to get the group ...
        if err == nil {
            // add the group to the channel
            select {
                case groups <- group:
            }
        }
    }()

    return groups, errc
}

这是读取通道进行计算的代码。

// Computes the results for the groups of data
func compute(done <-chan struct{}, groups <-chan model, c chan<- result) {
    for group := range groups {
        value := compute(group)

        select {
        case c <- result{value}:
        case <-done:
            return
        }
    }
}

【问题讨论】:

  • 找到这类事情的一个好方法是尝试使用 -race 运行它
  • -race 没有产生任何不同。 goroutines 被创建,所有的工作都完成了,然后它不会退出,因为一些 goroutines 还活着。
  • 你能发布一个可运行代码的要点吗?
  • 我在play.golang.org/p/T7zBCYLECp 创建了一个示例。看起来有死锁,但我不知道如何解决它。
  • 你为什么把groups &lt;- group写成select { case groups &lt;- group: }

标签: parallel-processing go goroutine


【解决方案1】:

因为您尝试从 errc 读取数据,除非出现错误,否则它是空的。

//编辑

如果没有错误,computeAll() 将始终阻止 &lt;- errc,另一种方法是使用类似:

func computeAll() (err error) {
    .........
    select {
    case err = <-errc:
    default: //don't block
    }
    return
}

【讨论】:

  • 我更新了代码,我实际上是使用单独的if语句来尝试读取errc。这并没有改变工人没有被取消的事实。还是您的意思有所不同?
  • if err := &lt;-errc; err != nil 正在阻塞,除非有东西关闭或向errc发送错误
  • 这并没有改变任何东西,代码仍然永远不会退出。
  • 就是这样。所以blog.golang.org/pipelines/bounded.go 的代码也有同样的问题吧?如果没有错误就会永远阻塞?
【解决方案2】:

如 OneOfOne 所说,尝试关闭 errc

go func() {
    wg.Wait()
    close(c)
    close(errc)
}()

// log the results of the computation
for r := range c { // log the results }

if err := range errc {
   if err != nil {
    return err
   }
}

【讨论】:

  • 无法关闭(errc),因为它是仅接收通道。更改 errc 处理(我假设您的意思是 for err := range errc)并没有改变任何东西。
猜你喜欢
  • 2021-10-16
  • 2021-04-23
  • 1970-01-01
  • 2021-03-14
  • 1970-01-01
  • 1970-01-01
  • 2020-03-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多