mirror of
https://github.com/teivah/100-go-mistakes.git
synced 2026-06-26 11:26:56 +08:00
feat: summary for mistake 70
This commit is contained in:
parent
ba249999cc
commit
75752b07c9
1 changed files with 56 additions and 0 deletions
|
|
@ -1684,6 +1684,62 @@ You should have a good reason to specify a channel size other than one for buffe
|
|||
|
||||
Remembering that slices and maps are pointers can prevent common data races.
|
||||
|
||||
Using mutext without carefully considering the underlying data structure can lead to data race.
|
||||
```go
|
||||
type Cache struct {
|
||||
mu sync.RWMutex
|
||||
balances map[string]float64
|
||||
}
|
||||
|
||||
func (c *Cache) AddBalance(id string, balance float64) {
|
||||
c.mu.Lock()
|
||||
c.balances[id] = balance
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func (c *Cache) AverageBalance() float64 {
|
||||
c.mu.RLock()
|
||||
balances := c.balances
|
||||
c.mu.RUnlock()
|
||||
sum := 0.
|
||||
for _, balance := range balances {
|
||||
sum += balance
|
||||
}
|
||||
return sum / float64(len(balances))
|
||||
}
|
||||
```
|
||||
If two goroutines, one calling `AddBalance` and another one calling `AverageBalance`, doing copy inside `RWMutext.RLock` and `RWMutext.RUnlock` can't prevent data race. Because `c.balances` is a **map** and internally it store the pointer to the array of key, value pairs. So doing `balances := c.balances` just copy the pointer which reference to the same array of key,value pairs. In order to prevent such data race problem, we can use one of the following techniques:
|
||||
* Protecting the whole funtion inside critical section
|
||||
* Protect only the copy part and do deep copy
|
||||
```go
|
||||
// protect the whole function
|
||||
func (c *Cache) AverageBalance() float64 {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
sum := 0.
|
||||
for _, balance := range c.balances {
|
||||
sum += balance
|
||||
}
|
||||
return sum / float64(len(c.balances))
|
||||
}
|
||||
|
||||
// protect only the copy part and do deep copy
|
||||
func (c *Cache) AverageBalance() float64 {
|
||||
c.mu.RLock()
|
||||
m := make(map[string]float64, len(c.balances))
|
||||
for k, v := range c.balances {
|
||||
m[k] = v
|
||||
}
|
||||
c.mu.RUnlock()
|
||||
sum := 0.
|
||||
for _, balance := range m {
|
||||
sum += balance
|
||||
}
|
||||
return sum / float64(len(m))
|
||||
}
|
||||
```
|
||||
We should choose the later technique if the operation(calculating `sum` in this case) isn't fast.
|
||||
|
||||
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/70-mutex-slices-maps/main.go)
|
||||
|
||||
### Misusing `sync.WaitGroup` (#71)
|
||||
|
|
|
|||
Loading…
Reference in a new issue