I’m new to Go and have been struggling with the below code; I’ve tried many different things but I can’t seem to get it to run without getting a “fatal error: all goroutines are asleep - deadlock!” error. What am I missing?
package main
import (
"fmt"
"sync"
"time"
)
func main() {
wg := new(sync.WaitGroup)
messages := make(chan string)
for x := 1; x <= 5; x++ {
wg.Add(1)
go sayhello(x, wg, &messages)
}
for msg := range messages {
fmt.Println(msg)
}
wg.Wait()
close(messages)
}
func sayhello(count int, wg *sync.WaitGroup, messages *chan string) {
defer wg.Done()
time.Sleep(time.Millisecond * time.Duration(1000))
*messages <- fmt.Sprintf("hello: %d", count)
}
Here is a modified version that worked for me. Comment out the line with close(messages) and it will deadlock. Using for range on a channel is the cause of the deadlock as it’ll never exit that loop.
package main
import (
"fmt"
"sync"
"time"
"log"
)
func main() {
wg := new(sync.WaitGroup)
messages := make(chan string)
for x := 1; x <= 5; x++ {
wg.Add(1)
go sayhello(x, wg, &messages)
}
go func(wg *sync.WaitGroup, messages chan string) {
log.Println("waiting")
wg.Wait()
log.Println("done waiting")
close(messages)
}(wg, messages)
for msg := range messages {
fmt.Println(msg)
}
}
func sayhello(count int, wg *sync.WaitGroup, messages *chan string) {
defer wg.Done()
time.Sleep(time.Millisecond * time.Duration(1000 * count))
*messages <- fmt.Sprintf("hello: %d", count)
log.Println("sent message ", count)
}
To explain the background of the problem: The range operator reads from the channel until the channel is closed. So in the original code, the for-range loop keeps waiting for more input from the channel, and wg.Wait() is never reached. Suneil’s solution separates reading the channel from waiting for the workers to finish.
package main
import (
"fmt"
"sync"
"time"
)
func main() {
wg := new(sync.WaitGroup)
messages := make(chan string, 5)
for x := 1; x <= 5; x++ {
wg.Add(1)
go sayhello(x, wg, &messages)
}
wg.Wait()
close(messages)
for msg := range messages {
fmt.Println(msg)
}
}
func sayhello(count int, wg *sync.WaitGroup, messages *chan string) {
defer wg.Done()
time.Sleep(time.Millisecond * time.Duration(1000))
*messages <- fmt.Sprintf("hello: %d", count)
}
Only one difference
You can want to consider mutex before use sleep in goroutines also
And one more
package main
import (
"fmt"
"strconv"
"sync"
)
var wg sync.WaitGroup
func main() {
message := make(chan string)
go sayhello(message)
for x := int64(1); x <= 5; x++ {
m := "hello : " + strconv.FormatInt(x, 10)
message <- m
}
wg.Wait()
}
func DisplayMsg(m string) {
defer wg.Done()
fmt.Printf("hello: %s\n", m)
return
}
func sayhello(message chan string) {
for {
m, more := <-message
wg.Add(1)
go DisplayMsg(m)
if more == false {
close(message)
return
}
}
}
Wouldn’t putting a mutex around a call to time.Sleep make the go routines appear to run serially? For example if 5 goroutines each mutex around a time.Sleep call of 1 second, then the first routine would wait 1 second, the second routine 2 seconds (waits for first routine to finish before it even starts sleeping)… and the fifth routine 5 seconds, rather than all five waiting for 1 second at the same time?