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