【问题标题】:How to wait until buffered channel (semaphore) is empty?如何等到缓冲通道(信号量)为空?
【发布时间】:2017-02-08 03:27:41
【问题描述】:

我有一个整数切片,它们是同时操作的:

ints := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

我使用缓冲通道作为信号量,以便获得同时运行的 go 例程的上限:

sem := make(chan struct{}, 2)

for _, i := range ints {
  // acquire semaphore
  sem <- struct{}{}

  // start long running go routine
  go func(id int, sem chan struct{}) {
    // do something

    // release semaphore
    <- sem
  }(i, sem)
}

在到达最后一个或最后两个整数之前,上面的代码运行良好,因为程序在最后一个 go 例程完成之前就结束了。

问题:如何等待缓冲通道耗尽?

【问题讨论】:

  • 你必须使用互斥锁或其他东西。缓冲的通道在满时会阻塞,但在它为空之前没有语言功能可以阻塞。

标签: go semaphore channel goroutine


【解决方案1】:

您不能以这种方式使用信号量(在本例中为通道)。无法保证在您处理值和调度更多 goroutine 时它​​不会为空。在这种情况下,这不是一个问题,特别是因为您正在同步调度工作,但是因为没有无竞争的方式来检查通道的长度,所以没有等待通道长度达到 0 的原语。

使用 sync.WaitGroup 等待所有 goroutine 完成

sem := make(chan struct{}, 2)

var wg sync.WaitGroup

for _, i := range ints {
    wg.Add(1)
    // acquire semaphore
    sem <- struct{}{}
    // start long running go routine
    go func(id int) {
        defer wg.Done()
        // do something
        // release semaphore
        <-sem
    }(i)
}

wg.Wait()

【讨论】:

  • 谢谢,我也考虑过使用WaitGroup。这感觉是正确的方法!
【解决方案2】:

显然没有人在等待你的 go-routines 完成。因此程序在最后 2 个 go-routines 完成之前结束。在程序结束之前,您可以使用工作组来等待您的所有 go-routines 完成。这更能说明问题 - https://nathanleclaire.com/blog/2014/02/15/how-to-wait-for-all-goroutines-to-finish-executing-before-continuing/

【讨论】:

  • 谢谢,我一直在寻找避免这种情况的方法,WorkGroup 完美运行。 JimB 的回答澄清了这一点
【解决方案3】:

使用“工作池”来处理您的数据。它比为 each int 运行 goroutine 更便宜,为其中的变量分配内存等等......

ints := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

ch := make(chan int)

var wg sync.WaitGroup

// run worker pool
for i := 2; i > 0; i-- {
    wg.Add(1)

    go func() {
        defer wg.Done()

        for id := range ch {
            // do something
            fmt.Println(id)
        }
    }()
}

// send ints to workers
for _, i := range ints {
    ch <- i
}

close(ch)

wg.Wait()

【讨论】:

    【解决方案4】:

    您可以在 for 循环中等待当前 goroutine 的“子 goroutines”

    semLimit := 2
    sem := make(chan struct{}, semLimit)
    
    for _, i := range ints {
      // acquire semaphore
      sem <- struct{}{}
    
      // start long running go routine
      go func(id int, sem chan struct{}) {
        // do something
    
        // release semaphore
        <- sem
      }(i, sem)
    }
    
    // wait semaphore
    for i := 0; i < semLimit; i++ { 
      wg<-struct{}{} 
    }
    
    

    还可以选择以import sync 的经济性编写一个简约的“信号等待组”

    semLimit := 2
    // mini semaphored waitgroup 
    wg := make(chan struct{}, semLimit)
    // mini methods
    wgAdd := func(){ wg<-struct{}{} }
    wgDone := func(){ <-wg }
    wgWait := func(){ for i := 0; i < semLimit; i++ { wgAdd() } }
    
    for _, i := range ints {
      // acquire semaphore
      wgAdd()
    
      // start long running go routine
      go func(id int, sem chan struct{}) {
        // do something
    
        // release semaphore
        wgDone()
      }(i, sem)
    }
    
    // wait semaphore
    wgWait()
    

    【讨论】:

      【解决方案5】:

      这是一个工作示例。最后的for 循环强制程序等待 直到工作完成:

      package main
      import "time"
      
      func w(n int, e chan error) {
         // do something
         println(n)
         time.Sleep(time.Second)
         // release semaphore
         <-e
      }
      
      func main() {
         a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
         e := make(chan error, 2)
         for _, n := range a {
            // acquire semaphore
            e <- nil
            // start long running go routine
            go w(n, e)
         }
         for n := cap(e); n > 0; n-- {
            e <- nil
         }
      }
      

      【讨论】:

        猜你喜欢
        • 2016-12-12
        • 2021-10-06
        • 1970-01-01
        • 2014-01-30
        • 2023-02-11
        • 2014-10-28
        • 2020-04-09
        相关资源
        最近更新 更多