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")
	}

map判断读写冲突流程

番外篇 &^ 运算

&^被定义为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