mirror of
https://github.com/teivah/100-go-mistakes.git
synced 2026-06-26 11:26:56 +08:00
feat: summary for mistake 74
This commit is contained in:
parent
668d6165fe
commit
f4e39d0f6f
1 changed files with 52 additions and 0 deletions
|
|
@ -1716,6 +1716,58 @@ You should have a good reason to specify a channel size other than one for buffe
|
||||||
|
|
||||||
`sync` types shouldn’t be copied.
|
`sync` types shouldn’t be copied.
|
||||||
|
|
||||||
|
Operations can't be in critical section if we accidentally **copy** the synchronization primitives.
|
||||||
|
`sync` types can be unintentinally copied in the following situation:
|
||||||
|
* Calling a method with a value receiver
|
||||||
|
* Calling a function with a sync argument
|
||||||
|
* Calling a function with an argument that contains a sync field
|
||||||
|
|
||||||
|
Suppose two goroutines try to increment the shared counter(which is `map[string]int`) in a critical section by using `sync.Mutex`:
|
||||||
|
```go
|
||||||
|
type Counter struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
counters map[string]int
|
||||||
|
}
|
||||||
|
func NewCounter() Counter {
|
||||||
|
return Counter{counters: map[string]int{}}
|
||||||
|
}
|
||||||
|
func (c Counter) Increment(name string){
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
c.counters[name]++
|
||||||
|
}
|
||||||
|
```
|
||||||
|
If two goroutines try to increment the counter there will be a data race:
|
||||||
|
```go
|
||||||
|
counter := NewCounter()
|
||||||
|
go func() {
|
||||||
|
counter.Increment("foo")
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
counter.Increment("bar")
|
||||||
|
}()
|
||||||
|
```
|
||||||
|
The reason is since the receiver of `Increment` method is a value receiver, so everytime `Increment` method get called it will receive the copy of `Counter` struct which also copy the `mu`. In order to shared the same `sync.Mutext`, we can change the receiver type to **pointer** or we can use `*sync.Mutext`:
|
||||||
|
```go
|
||||||
|
// use pointer receiver
|
||||||
|
func (c *Counter) Increment(name string) {
|
||||||
|
// Same code
|
||||||
|
}
|
||||||
|
|
||||||
|
// change sync.Mutext to pointer
|
||||||
|
type Counter struct {
|
||||||
|
mu *sync.Mutex
|
||||||
|
counters map[string]int
|
||||||
|
}
|
||||||
|
func NewCounter() Counter {
|
||||||
|
return Counter{
|
||||||
|
mu: &sync.Mutex{}, // need to explicitly initialize "mu" since zero value of pointer is nil.
|
||||||
|
counters: map[string]int{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/74-copying-sync/main.go)
|
[Source code :simple-github:](https://github.com/teivah/100-go-mistakes/tree/master/src/09-concurrency-practice/74-copying-sync/main.go)
|
||||||
|
|
||||||
## Standard Library
|
## Standard Library
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue