8.2 Go 锁

8.2 Go 锁

案例(坑):多个goroutine操作同一个map。

go提供了一种叫map的数据结构,可以翻译成映射,对应于其他语言的字典、哈希表。借助map,可以定义一个键和值,然后可以从map中获取、设置和删除这个值,尤其适合数据查找的场景。

但是map的使用有一定的限制,如果是在单个协程中读写map,那么不会存在什么问题,如果是多个协程并发访问一个map,有可能会导致程序退出,并打印下面错误信息。

fatal error: concurrent map writes

错误案例代码

package main

var myMap = make(map[int]int, 10)

//要求计算50的阶乘结果
//阶乘就是1*2*3*4...50 =?

func test(n int) {
    //定义初始值1
    res := 1
    //每次循环进行阶乘
    for i := 0; i <= n; i++ {
        res *= i
    }
    //最终计算结果,写入map
    //由于多个协程同时操作map,引发资源竞争报错
    myMap[n] = res

}
func main() {
    //开启50个协程
    for i := 0; i < 10; i++ {
        go test(10)
    }
}

并发访问map是不安全的操作,在协程中访问map,必须提供某种同步资源机制,使用sync.Mutex互斥锁同步解决协程的竞争问题。

package main

import (
    "fmt"
    "sync"
    "time"
)

var lock sync.Mutex

func Printer(str string) {
    //我现在开始使用打印机了,其他人都等我完事了再来
    lock.Lock()
    for _, data := range str {
        fmt.Printf("%c", data)
        time.Sleep(time.Second)
    }
    //我完事了,你们上吧
    lock.Unlock()
    fmt.Printf("\n")
}

func Alex() {
    Printer("hello")
}
func Wupeiqi() {
    Printer("oldboy")
}

func main() {
    //coffe(10)//单线程执行函数
    go Alex()
    go Wupeiqi()
    //主线程等待协程结束后 再退出
    time.Sleep(time.Second * 10)
}

结论:

1.可以使用加锁的方式解决goroutine的通讯。
2.主线程等待所有goroutine的时间难以确定,设置固定的等待时间肯定不合理。
3.对全局变量加锁同步来通讯,也不利于多个协程对变量的读写。
4.要让主线程等待所有goroutine退出后在退出,如何知道所有goroutine都退出了呢?
5.因此,channel应运而生!!
posted @ 2019-07-27 21:45  笑得好美  阅读(97)  评论(0编辑  收藏  举报