等待组和无缓冲通道的竞争状况

问题描述:

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)