map如何知道自己被抢占
map的底层实现是hmap结构体,有一个叫flags的uint8类型字段。
下面这些常量表示flag每一个位的作用
// flags
iterator = 1 // there may be an iterator using buckets
oldIterator = 2 // there may be an iterator using oldbuckets
hashWriting = 4 // a goroutine is writing to the map
sameSizeGrow = 8 // the current map growth is to a new map of the same size
其中这里关于如何判断锁冲突的是hashWriting字段
写
// 因为h.flags默认为0,所以hashWriting初始化应该为0,如果不为0说明其他goroutine在写
if h.flags&hashWriting != 0 {
fatal("concurrent map writes")
}
// 开始写,writing位置为1
h.flags ^= hashWriting
// 写之后判断,因为刚刚已经把writting置位1了,这里不应该为0,如果为0说明在写的过程中有人改过了。
if h.flags&hashWriting == 0 {
fatal("concurrent map writes")
}
// 恢复原状
h.flags &^= hashWriting
删除
// 判断是否为0,如果不为0则fatal
if h.flags&hashWriting != 0 {
fatal("concurrent map writes")
}
// 开始删除,置为1
h.flags ^= hashWriting
// 如果等于0表示又其他go routine把它置为1了,fatal
if h.flags&hashWriting == 0 {
fatal("concurrent map writes")
}
// 恢复原状
h.flags &^= hashWriting
读
// 读的时候只需要判断,不需要修改。
if h.flags&hashWriting != 0 {
fatal("concurrent map read and map write")
}
番外篇 &^ 运算
&^被定义为bit clear运算,表示用右边的操作数清空左边的操作数的指定位。
0110 &^ 0010 表示清空0110的右数第2位。清空之后变成0100
package main
import "fmt"
func test(testCases [][]int) {
for _, testCase := range testCases {
ret := testCase[0] &^ testCase[1]
fmt.Printf("%v &^ %v = %v\n", testCase[0], testCase[1], ret)
}
}
func main() {
testcase := [][]int{
{0, 0},
{0, 1},
{1, 0},
{1, 1},
{6, 4},
}
test(testcase)
}
结果
0 &^ 0 = 0
0 &^ 1 = 0
1 &^ 0 = 1
1 &^ 1 = 0
6 &^ 4 = 2