golang slice 并发写入#
由于 slice/map 是引用类型,golang 函数是传值调用,所用参数副本依然是原来的 slice, 并发访问同一个资源会导致竟态条件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| package main
import (
"fmt"
"sync"
)
func main() {
var (
slc = []int{}
n = 10
wg sync.WaitGroup
)
wg.Add(n)
for i := 0; i < n; i++ {
go func() {
slc = append(slc, i)
wg.Done()
}()
}
wg.Wait()
fmt.Println("len:", len(slc))
fmt.Println("done")
}
(每次执行都会有不同的结果)
out:
len: 3
done
|
加锁来解决并发#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| package main
import (
"fmt"
"sync"
)
func main() {
var (
slc []int
n = 10
wg sync.WaitGroup
)
mutex := sync.Mutex{}
wg.Add(n)
for i := 0; i < n; i++ {
go func() {
mutex.Lock()
slc = append(slc, i)
mutex.Unlock()
wg.Done()
}()
}
wg.Wait()
fmt.Println("len:", len(slc))
fmt.Println("done")
}
|
Chan 来解决并发#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| package main
import (
"fmt"
"sync"
)
func main(){
var (
slc []int
n = 100
wg sync.WaitGroup
)
ch := make(chan int)
doneCh := make(chan struct{})
go func() {
for v := range ch {
slc = append(slc, v)
}
doneCh <- struct{}{}
}()
wg.Add(n)
for i := 0; i < n; i++ {
tmp := i
go func() {
ch <- tmp
wg.Done()
}()
}
wg.Wait()
close(ch)
<-doneCh
fmt.Println("len:", len(slc))
fmt.Println("done")
}
|
基准测试#
留个疑点:这里无锁方案反而性能比不过有锁的。
goos | 操作系统,这里是windows |
---|
goarch | CPU架构,这里是64位X86 |
pkg | package名,可以在测试的时候指定package |
cpu | CPU的信息,这里可以看到是12代酷睿i5 |
- ns/op 代表每次执行逻辑消耗的时间
- B/op 代表每次执行逻辑消耗的内存
- allocs/op代表每次执行逻辑申请内存的次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| package main
import "testing"
func BenchmarkLock20(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Lock()
}
})
}
func BenchmarkNoLock20(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
noLock()
}
})
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| $ go test -count=3 -benchtime=5s -benchmem -bench .
goos: windows
goarch: amd64
pkg: xxxx
cpu: 12th Gen Intel(R) Core(TM) i5-1240P
BenchmarkLock20-16 462022(执行次数) 12215 ns/op 6915 B/op 111 allocs/op
BenchmarkLock20-16 477160(执行次数) 12997 ns/op 6909 B/op 111 allocs/op
BenchmarkLock20-16 335850(执行次数) 16865 ns/op 6908 B/op 111 allocs/op
BenchmarkNoLock20-16 354907(执行次数) 18361 ns/op 5585 B/op 113 allocs/op
BenchmarkNoLock20-16 334875(执行次数) 18441 ns/op 5587 B/op 113 allocs/op
BenchmarkNoLock20-16 317461(执行次数) 18732 ns/op 5591 B/op 113 allocs/op
PASS
ok xxx 37.195s
|