go

Golang what's wrong with this program (Waitgroups and channels)


I have the following code. It's a learning exercise, so it doesn't do anything obviously useful. If the wg.Add value is 1, it works. If the wg.Add value is 2, it fails. If I remove the channel logic, it works with a wg.Add value of 2.

Any ideas on what I'm doing wrong?

package main

import (
    "fmt"
    "sync"
)

func popMessage(wg *sync.WaitGroup, m *sync.Mutex, i *int, c chan<- int) {
    m.Lock()
    *i++
    c <- *i
    m.Unlock()
    fmt.Printf("pop %d\n", *i)
    wg.Done()
}

func main() {
    var wg sync.WaitGroup
    var m sync.Mutex
    var intPtr *int

    c := make(chan int, 1)
    wg.Add(2)

    intPtr = new(int)
    *intPtr = 1
    go popMessage(&wg, &m, intPtr, c)
    go popMessage(&wg, &m, intPtr, c)
    go popMessage(&wg, &m, intPtr, c)
    wg.Wait()
    fmt.Printf("final %d\n", *intPtr)
    for x := range c {
        fmt.Printf("channel %d\n", x)
    }
}

Solution

  • The wg.Wait() function will block until the WaitGroup counter reaches zero. In your case, you initialized the counter with wg.Add(2), meaning wg.Wait() will wait for wg.Done() to be called twice before it proceeds.

    You also created a buffered channel with a capacity of 1 (c := make(chan int, 1)). This means the channel can hold only one value in its buffer. Once the buffer is full, any additional send operation (c <- value) will block until a goroutine receives a value from the channel (<-c), freeing up space in the buffer.

    In your code, after one value is sent to the channel, the buffer becomes full. Any subsequent send operations (c <- *i) will block because the channel has no available space. As a result, only one goroutine successfully calls wg.Done(), while the other goroutines remain blocked, waiting to send their data.

    This leads to a deadlock: wg.Wait() is waiting for the second wg.Done() call, but the second goroutine is stuck trying to send data to the channel. Consequently, your program hangs indefinitely.

    FIX:

    c := make(chan int, 3)
    wg.Add(3)
    

    And write this code above the for-loop and under wg.Wait():

    close(c)