【问题标题】:Trying to end goroutine using close(ch) but end up running infinitely尝试使用 close(ch) 结束 goroutine 但最终无限运行
【发布时间】:2019-07-24 16:11:14
【问题描述】:

一旦另一个 goroutine 关闭一个通道,我正在尝试结束多个 goroutine。但是,在收到关闭信号后,我将陷入无限循环。我不知道为什么。

我知道使用 context.Context 是可能的,但我正在尝试关闭频道。

去游乐场:https://play.golang.org/p/C6pcYgGLnG9

package main

import (
    "fmt"
    "time"
    "sync"
)


func runner(id int, ch <-chan struct{}, wg *sync.WaitGroup) {
    for {
        select {
            case <-time.Tick(time.Second):
                fmt.Println("worker ", id)
            case <- ch:
                fmt.Println("closing worker ", id)
                break
        }
    }

    wg.Done()

}

func main() {
    fmt.Println("Hello, playground")
    ch := make(chan struct{})
    var wg sync.WaitGroup

    wg.Add(1)
    go runner(1, ch, &wg)

    wg.Add(1)
    go runner(2, ch, &wg)

    time.Sleep(5*time.Second)

    close(ch)

    wg.Wait()
}

【问题讨论】:

    标签: go


    【解决方案1】:

    问题是你break的范围:

    func runner(id int, ch <-chan struct{}, wg *sync.WaitGroup) {
        for {
            select {
                case <-time.Tick(time.Second):
                    fmt.Println("worker ", id)
                case <- ch:
                    fmt.Println("closing worker ", id)
                    break
            }
        }
    
        wg.Done()
    }
    

    您想跳出 for 循环,但实际上您只是跳出 @9​​87654323@。要解决此问题,您有两种选择:

    1. 为你的 for 循环添加一个标签,然后明确地中断它:

      func runner(id int, ch <-chan struct{}, wg *sync.WaitGroup) {
      loop:  // <---------- add a label
          for {
              select {
              case <-time.Tick(time.Second):
                  fmt.Println("worker ", id)
              case <-ch:
                  fmt.Println("closing worker ", id)
                  break loop  // <---------- and break from it explicitly
              }
          }
      
          wg.Done()
      }
      
    2. 可能是一个更惯用和更强大的解决方案,完成后只需返回即可。这意味着必须推迟 wg.Done() 调用。

      func runner(id int, ch <-chan struct{}, wg *sync.WaitGroup) {
          defer wg.Done() // <--- Defer the wg.Done() call, so it happens on return
          for {
              select {
              case <-time.Tick(time.Second):
                  fmt.Println("worker ", id)
              case <-ch:
                  fmt.Println("closing worker ", id)
                  return   // <--- replace `break` with `return`
              }
          }
      }
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-29
      • 2021-10-11
      • 2011-03-23
      • 2021-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多