前言
sync.Mapmap+sync.Mutexmap+sync.RWMutex
注:使用的Go版本是1.18
一个并发写map的例子
package main
import (
"fmt"
)
func main() {
m := map[int]bool{}
go func() {
defer func() {
a := recover()
fmt.Println("1", a)
}()
for {
m[10] = true
}
}()
go func() {
defer func() {
a := recover()
fmt.Println("2", a)
}()
for {
m[10] = true
}
}()
select {}
}
复制代码
这个例子是两个goroutine并发写一个map的一个key,在对于的函数里,使用了defer+recover()试图捕获并发写map产生的异常,主goroutine通过select{}进行阻塞等待。
我们运行这段代码,会产生以下错误信息:
fatal error: concurrent map writes
goroutine 7 [running]:
runtime.throw({0x2c7b35?, 0x2bc2c0?}) <-重点
C:/Program Files/Go/src/runtime/panic.go:992 +0x76 fp=0xc000051f68 sp=0xc000051f38 pc=0x253076
runtime.mapassign_fast64(0x2b65c0, 0xc000100480, 0xa) <-重点
C:/Program Files/Go/src/runtime/map_fast64.go:177 +0x2b4 fp=0xc000051fa0 sp=0xc000051f68 pc=0x22fbd4
main.main.func2()
D:/Github/him/_test/test.go:24 +0x50 fp=0xc000051fe0 sp=0xc000051fa0 pc=0x2ab770
runtime.goexit()
C:/Program Files/Go/src/runtime/asm_amd64.s:1571 +0x1 fp=0xc000051fe8 sp=0xc000051fe0 pc=0x27be81
created by main.main
D:/Github/him/_test/test.go:18 +0x7d
goroutine 1 [select (no cases)]:
main.main()
D:/Github/him/_test/test.go:27 +0x85
goroutine 6 [runnable]:
main.maiD:/Github/him/_test/test.go:15 +0x50
created by main.main
D:/Github/him/_test/test.go:9 +0x50
Process finished with the exit code 2
复制代码
mapassign_fast64()runtime.throw()
我们先看看runtime/map_fast64.go的mapassign_fast64()函数:
func mapassign_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
// 省略代码。。。
// 第一处
if h.flags&hashWriting != 0 {
throw("concurrent map writes")
}
// 第二处
h.flags ^= hashWriting
// 省略代码。。。
again:
// 省略代码。。。
bucketloop:
// 省略代码。。。
done:
// 省略代码。。。
// 第三处
if h.flags&hashWriting == 0 {
throw("concurrent map writes")
}
// 第四处
h.flags &^= hashWriting
return elem
}
复制代码
其中:
hashWriting = 4 // 表示有 goroutine 正在写map
复制代码
hashWritinghashWriting
我们按照上面分的4个位置:
第一处
if h.flags&hashWriting != 0 {
throw("concurrent map writes")
}
复制代码
在函数的入口处,先检查是否有goroutine正在写map,如果有说明并发写map,直接throw()
第二处
h.flags ^= hashWriting
复制代码
异或操作或操作第一处h.flagshashWriting第三处
第三处
if h.flags&hashWriting == 0 {
throw("concurrent map writes")
}
复制代码
h.flags&hashWriting第二步h.flags ^= hashWritingh.flagshashWriting
第四处
h.flags &^= hashWriting
复制代码
&^
第一个操作数 | 第二个操作数 | 结果 | 解释 |
---|---|---|---|
0 | 0 | 0 | 0&^0 |
0 | 1 | 0 | 0&^1 |
1 | 0 | 1 | 1&^0 |
1 | 1 | 0 | 1&^1 |
除了hashWriting对应的位h.flagshashWriting对应的位h.flags
h.flagshashWriting
简单来说
h.flagsthrow()
throw()
runtime.throw()
func throw(s string) {
systemstack(func() {
print("fatal error: ", s, "\n")
})
gp := getg()
if gp.m.throwing == 0 {
gp.m.throwing = 1
}
fatalthrow()
*(*int)(nil) = 0 // not reached
}
复制代码
fatal error: xxxfatalthrow()fatalthrow()
runtime.fatalthrow()
我们直接看这个函数的注释:
// fatalthrow implements an unrecoverable runtime throw. It freezes the
// system, prints stack traces starting from its caller, and terminates the
// process.
复制代码
简单来说就是:
fatalthrow()recover()
fatalthrow()exit(2)Process finished with the exit code 2
recover()recover()
总结
runtime.throw()recover()sync.Mutexsync.RWMutextsync.Map