I am a learner to GoLang and was trying to experiment and learn about Sync package and chan concept.
Follwoing is the code I am running and expecting to print Receiving Channel Value on console, but the value is not getting printed, it print the values sometimes but not always.
If I range through the chan without putting it into the go routine then its printing all the channel values but failing with an error "fatal error: all goroutines are asleep - deadlock!"
I tried synchronizing the channel reading using channel "done", but in that case it again started failing with same error. I also tried waitGroup API as well which you can see in my code (commented), but that also didn't worked for me.
Thanks for the help
Source Code:
package main
import (
"fmt"
"sync"
)
type safeOperation struct {
i int
sync.Mutex
}
var wg sync.WaitGroup
func main() {
so := new(safeOperation)
ch := make(chan int)
//done := make(chan bool)
for i := 0; i < 5; i++ {
go so.Increment(ch)
go so.Decrement(ch)
}
go func() {
//wg.Add(1)
for c := range ch {
fmt.Println("Receiving Channel Value: ", c)
}
//wg.Done()
//done <- true
}()
//wg.Wait()
//<-done
fmt.Println("Value: ", so.GetValue())
fmt.Println("Main method finished")
}
func (so *safeOperation) Increment(ch chan int) {
//so.Lock()
//defer wg.Done()
so.i++
ch <- so.i
//so.Unlock()
}
func (so *safeOperation) Decrement(ch chan int) {
//so.Lock()
//defer wg.Done()
so.i--
ch <- so.i
//so.Unlock()
}
func (so *safeOperation) GetValue() int {
so.Lock()
v := so.i
so.Unlock()
return v
}
Output: Value: 1 Main method finished
A good pattern for using WaitGroup
is to call Add()
before you send to the channel or use the go
keyword, and call Done()
after receiving from the channel. This way you can be sure that Add()
is always called on-time, regardless of whether sending on the channel blocks.
I have changed your example code to do this:
package main
import (
"fmt"
"sync"
)
type safeOperation struct {
i int
sync.Mutex
}
var wg sync.WaitGroup
func main() {
so := new(safeOperation)
ch := make(chan int)
for i := 0; i < 5; i++ {
wg.Add(1)
go so.Increment(ch)
wg.Add(1)
go so.Decrement(ch)
}
go func() {
for c := range ch {
fmt.Println("Receiving Channel Value: ", c)
wg.Done()
}
}()
wg.Wait()
//<-done
fmt.Println("Value: ", so.GetValue())
fmt.Println("Main method finished")
}
func (so *safeOperation) Increment(ch chan int) {
so.i++
ch <- so.i
}
func (so *safeOperation) Decrement(ch chan int) {
so.i--
ch <- so.i
}
func (so *safeOperation) GetValue() int {
so.Lock()
v := so.i
so.Unlock()
return v
}
Of course you also want to protect safeOperation.i
with a mutex (or its value will be unpredictable), but this is all that is necessary to get the desired output.