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。

results matching ""

    No results matching ""