How to wait until buffered channel (semaphore) is empty?

Kiril picture Kiril · Sep 29, 2016 · Viewed 11.3k times · Source

I have a slice of integers, which are manipulated concurrently:

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

I'm using a buffered channel as semaphore in order to have a an upper bound of concurrently running go routines:

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)
}

The code above works pretty well until the last or last two integers are reached, because the program ends before those last go routines are finished.

Question: how do I wait for the buffered channel to drain?

Answer

JimB picture JimB · Sep 29, 2016

You can't use a semaphore (channel in this case) in that manner. There's no guarantee it won't be empty any point while you are processing values and dispatching more goroutines. That's not a concern in this case specifically since you're dispatching work synchronously, but because there's no race-free way to check a channel's length, there's no primitive to wait for a channel's length to reach 0.

Use a sync.WaitGroup to wait for all goroutines to complete

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()