Search code examples
gosemaphorechannelgoroutine

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


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 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?


Solution

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