After getting (the right) solution to my initial problem in this post Understanding golang channels: deadlock, I have come up with a slightly different solution (which in my opinion reads better:
// Binary histogram counts the occurences of each word.
package main
import (
"fmt"
"strings"
"sync"
)
var data = []string{
"The yellow fish swims slowly in the water",
"The brown dog barks loudly after a drink ...",
"The dark bird bird of prey lands on a small ...",
}
func main() {
histogram := make(map[string]int)
words := make(chan string)
var wg sync.WaitGroup
for _, line := range data {
wg.Add(1)
go func(l string) {
for _, w := range strings.Split(l, " ") {
words <- w
}
wg.Done()
}(line)
}
go func() {
for w := range words {
histogram[w]++
}
}()
wg.Wait()
close(words)
fmt.Println(histogram)
}
It does work, but unfortunately running it against race, it shows 2 race conditions:
==================
WARNING: DATA RACE
Read at 0x00c420082180 by main goroutine:
...
Previous write at 0x00c420082180 by goroutine 9:
...
Goroutine 9 (running) created at:
main.main()
Can you help me understand where is the race condition?
You are trying to read from histogram
in fmt.Println(histogram)
which is not synchronized to the write of the goroutine mutating it histogram[w]++
. You can add a lock to synchronize the writes and reads.
e.g.
var lock sync.Mutex
go func() {
lock.Lock()
defer lock.Unlock()
for w := range words {
histogram[w]++
}
}()
//...
lock.Lock()
fmt.Println(histogram)
Note you can also use a sync.RWMutex
.
Another thing you could do is to wait for the goroutine mutating histogram
to finish.
var histWG sync.WaitGroup
histWG.Add(1)
go func() {
for w := range words {
histogram[w]++
}
histWG.Done()
}()
wg.Wait()
close(words)
histWG.Wait()
fmt.Println(histogram)
Or simply use a channel to wait.
done := make(chan bool)
go func() {
for w := range words {
histogram[w]++
}
done <- true
}()
wg.Wait()
close(words)
<-done
fmt.Println(histogram)