)
Go并发编程中的同步控制sync包Mutex、RWMutex、WaitGroup、Once文章目录1.Mutex2.RWMutex3.WaitGroup4.Once5.共享资源保护练习1.Mutex保护共享map: 学生成绩录入系统map[name]score同时录入多个学生成绩2.WaitGroup等待多个任务完成3.使用 sync.Once实现配置初始化提示这一部分涉及到操作系统—锁的知识需要导入包 sync1.Mutex互斥锁拿到锁之后可以读可以写准确说法Mutex保证同一时间只有一个goroutine访问临界区可以进行读或写操作定义互斥锁调用方法lock上锁、unlock解锁importsync// 导入包vara sync.Mutex// 定义互斥锁a.Lock()// 上锁a.Unlock()// 解锁2.RWMutex读写锁读者写者问题可以同时多个读(即读读)写写、读写都不可以适用于读多写少定义读写锁调用方法lock unlock rlock runlock读锁—多个goroutine可以同时读取varb sync.RWMutex// 定义读写锁b.RLock()// 读锁允许多个同时读b.RUnlock()// 释放读锁写锁—写操作独占资源varb sync.RWMutex// 定义读写锁b.Lock()// 写锁同一时刻只允许一个写b.Unlock()// 释放写锁3.WaitGroup有时候会遇到main goroutine 结束了导致主函数中的goroutine 还没运行完也直接结束了WaitGroup就是解决这个问题的它能让主函数中的goroutine全部结束之后main goroutine 才继续执行WaitGroup可以理解为一个计数器用来记录还有多少个goroutine没有完成用于等待多个goroutine执行完成防止main goroutine提前结束var wg sync.WaitGroup定义一个计数器wgwg.Add(5)写在主函数里面main goroutine 需要等待5个任务wg.Done()写在功能函数里面表示这个任务完成计数器-1wg.Wait()写在主函数表示等到计数器变为0时再继续执行4.Once这样一个场景查询函数里面要先初始化数据库连接然后进行查询如果多个goroutine都调用查询那就需要多次初始化数据库连接但是初始化一次就够了要解决这个问题就要用到OnceOnce 是只允许某个函数执行一次的同步工具防止函数内部逻辑执行多次varonce sync.Once// 定义once.Do(函数名)// 调用Do方法5.共享资源保护Go有两种解决资源竞争的方法1共享内存锁互斥访问共享资源就会用到锁 Mutex RWMutex通过上锁、解锁来保证共享资源的安全访问2通道传递数据Go哲学不要通过共享内存通信而要通过通信来共享内存练习1.Mutex保护共享map: 学生成绩录入系统map[name]score同时录入多个学生成绩packagemainimport(fmtsynctime)// map学生姓名-成绩var(scoresmake(map[string]int)lock sync.Mutex)// 录入成绩函数funcaddScore(namestring,scoreint){// 对map加锁lock.Lock()// 修改mapscores[name]score// 解锁lock.Unlock()}funcmain(){goaddScore(Tom,90)goaddScore(Jack,80)goaddScore(Alice,95)goaddScore(Bob,70)goaddScore(Lucy,88)time.Sleep(3*time.Second)fmt.Println(scores)}输出map[Alice:95Bob:70Jack:80Lucy:88Tom:90]2.WaitGroup等待多个任务完成学生信息缓存系统要求多个 goroutine 查询学生成绩读一个 goroutine 修改学生成绩写使用 RWMutex 保护 map使用 WaitGroup 等待所有 goroutine 完成packagemainimport(fmtsync)// map学生姓名-成绩var(scoresmake(map[string]int)rwlock sync.RWMutex wg sync.WaitGroup)// 修改成绩funcaddScore(namestring,scoreint){deferwg.Done()// 对map加锁rwlock.Lock()// 本函数结束之前解锁deferrwlock.Unlock()// 修改mapscores[name]score}// 查询成绩funcsearch(namestring){deferwg.Done()rwlock.RLock()deferrwlock.RUnlock()ifscore,ok:scores[name];ok{fmt.Println(name,score)}else{fmt.Println(成绩未录入)}}funcmain(){scores[Tom]88scores[Tack]90scores[Alice]77scores[Bob]87scores[Lucy]89// 等待5个任务wg.Add(5)// 1个写任务把Tom的成绩改为90goaddScore(Tom,90)// 4个读任务查看Tom、Jack、Bob、Lucy的成绩gosearch(Tom)gosearch(Jack)gosearch(Bob)gosearch(Lucy)// 等待全部完成再继续执行wg.Wait()fmt.Println(scores)}输出Lucy89Tom90Bob87成绩未录入map[Alice:77Bob:87Lucy:89Tack:90Tom:90]3.使用 sync.Once实现配置初始化要求模拟服务器启动时加载配置loadConfig()启动10个goroutine每个协程调用loadConfig()需要输出配置加载成功任务执行任务执行任务执行…“配置加载成功” 只能出现一次packagemainimport(fmtsync)// 定义once wgvaronce sync.Oncevarwg sync.WaitGroup// 加载配置funcloadConfig(){fmt.Println(配置加载成功)}// 任务执行funcworking(){deferwg.Done()once.Do(loadConfig)fmt.Println(任务执行)}funcmain(){wg.Add(10)fori:0;i10;i{goworking()}wg.Wait()fmt.Println(10个任务执行完成!)}输出配置加载成功 任务执行 任务执行 任务执行 任务执行 任务执行 任务执行 任务执行 任务执行 任务执行 任务执行10个任务执行完成!