1.1.1. 一、为什么需要全局锁和管道。
当我们开启多个协程同时并发修改一个全局变量。会出现修改错误。如下:
//全局map
var resultMap = make(map[int]int, 10)
// 更新map
func UpdateMap(n1 int) {
time.Sleep(time.Second)
resultMap[n1] = n1
}
func main() {
for i := 0; i < 200; i++ {
go UpdateMap(i) //并发写map,运行后出错:fatal error: concurrent map writes
}
time.Sleep(time.Second * 200)
//这里我们输出结果,变量这个结果
for i, v := range resultMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
}
运行后会出现:concurrent map writes
的错误,并且程序异常退出。如下:
fatal error: concurrent map writes
fatal error: concurrent map writes
fatal error: concurrent map writes
goroutine 264 [running]:
runtime.throw(0x4c9a6c, 0x15)
G:/Go/src/runtime/panic.go:617 +0x79 fp=0xc00028df68 sp=0xc00028df38 pc=0x42af39
runtime.mapassign_fast64(0x4abaa0, 0xc0000742d0, 0xc5, 0x0)
G:/Go/src/runtime/map_fast64.go:101 +0x366 fp=0xc00028dfa8 sp=0xc00028df68 pc=0x40f6d6
说明:
1、因为多个协程并发修改同一个变量。出现了错误。所以需要给全局变量加互斥锁,或者使用channel通信。
2、可以使用
go build -race main.go
编译程序;然后运行exe文件。会显示有多少个并发修改错误。(这个我没有测试出来)。
1.1.2. 二、全局互斥锁解决资源竞争。
互斥锁是 sync 包的 Mutex 结构体。可以声明Mutex 结构体的变量对代码加锁。如下:
var (
resultMap = make(map[int]int, 10)
// sync包的 Mutex 结构体。声明一个全局互斥锁
// 声明一个全局变量myLock,是sync包的 Mutex 结构体的类型。
myLock sync.Mutex
)
// 更新map
func UpdateMap(n1 int) {
time.Sleep(time.Millisecond)
// 加锁
myLock.Lock()
resultMap[n1] = n1
// 解锁
myLock.Unlock()
}
func main() {
for i := 0; i < 200; i++ {
go UpdateMap(i) //并发写map
}
time.Sleep(time.Millisecond * 200)
myLock.Lock() //加锁
for i, v := range resultMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
myLock.Unlock() //解锁
}
说明:
1、声明一个全局变量
myLock sync.Mutex
,调用myLock.Lock()
加锁,调用myLock.Unlock()
解锁。2、myLock是sync.Mutex 的实例。某个代码执行myLock.Lock() 加锁了之后,其他代码如果执行到代码 myLock.Lock() ,必须等待解锁操作 myLock.Unlock() 完成后。才能执行加锁操作 myLock.Lock()。
3、主线程读取resultMap也必须加锁。否则主线程也会和其他协程竞争resultMap。