Golang基础笔记十五之sync
本文首发于公众号:Hunter后端原文链接:Golang基础笔记十五之sync
这一篇笔记介绍 Golang 中的 sync 模块。
sync 包主要提供了基础的同步原语,比如互斥锁,读写锁,等待组等,用于解决并发编程中的线程安全问题,以下是本篇笔记目录:
[*]WaitGroup-等待组
[*]sync.Mutex-互斥锁
[*]sync.RWMutex-读写锁
[*]sync.Once-一次性执行
[*]sync.Pool-对象池
[*]sync.Cond-条件变量
[*]sync.Map
1、WaitGroup-等待组
前面在第十篇我们介绍 goroutine 和 channel 的时候,在使用 goroutine 的时候介绍有一段代码如下:
package main
import (
"fmt"
"time"
)
func PrintGoroutineInfo() {
fmt.Println("msg from goroutine")
}
func main() {
go PrintGoroutineInfo()
time.Sleep(1 * time.Millisecond)
fmt.Println("msg from main")
}在这里,我们开启了一个协程调用 PrintGoroutineInfo() 函数,然后使用 time.Sleep() 来等待它调用结束。
然而在开发中,我们不能确定这个函数多久才能调用完毕,也无法使用准确的 sleep 时间来等待,那么这里就可以使用到 sync 模块的 WaitGroup 函数来等待一个或多个 goroutine 执行完毕。
下面是使用示例:
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func SleepRandSeconds(wg *sync.WaitGroup) {
defer wg.Done()
sleepSeconds := rand.Intn(3)
fmt.Printf("sleep %d seconds\n", sleepSeconds)
time.Sleep(time.Duration(sleepSeconds) * time.Second)
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go SleepRandSeconds(&wg)
go SleepRandSeconds(&wg)
wg.Wait()
fmt.Println("函数执行完毕")
}在这里,我们通过 var wg sync.WaitGroup 定义了一个等待组,并通过 wg.Add(2) 表示添加了需要等待的并发数,在并发中我们将 &wg 传入并通过 wg.Done() 减少需要等待的并发数。
在 wg.Done() 函数内部,使用 wg.Add(-1) 减少需要等待的并发数,在 main 函数中,使用 wg.Wait() 进入阻塞状态,当等待的并发都完成后,此函数就会返回,完成等待并接着往后执行。
2、sync.Mutex-互斥锁
1. 数据竞态与互斥锁
当多个 goroutine 并发访问同一个共享资源,且至少有一个访问是写操作时,就会发生数据竞态,造成的结果就是程序每次运行的结果表现可能会不一致。
比如下面的示例:
var balance int
func AddFunc() {
balance += 1
}
func main() {
for range 100 {
go AddFunc()
}
time.Sleep(5 * time.Second)
fmt.Println("balance is: ", balance)
}多次执行上面的代码,最终输出的 balance 的值可能都不一致。
如果一个变量在多个 goroutine 同时访问时,不会出现比如数据不一致或程序崩溃的情况,那么我们就称其是并发安全的。
我们可以使用 go run -race main.go 的方式来检测数据竞态,执行检测后,会输出数据竞态的一些信息,比如发生在代码的多少行,一共发生了多少次数据竞态:
==================
WARNING: DATA RACE
Read at 0x000003910df8 by goroutine 7:
main.AddFunc()
/../main.go:13 +0x24
Previous write at 0x000003910df8 by goroutine 6:
main.AddFunc()
/../main.go:13 +0x3c
Goroutine 7 (running) created at:
main.main()
/../main.go:18 +0x32
Goroutine 6 (finished) created at:
main.main()
/../main.go:18 +0x32
==================
balance is:98
Found 3 data race(s)
exit status 66而要避免这种数据竞态的发生,我们可以限制在同一时间只能有一个 goroutine 访问同一个变量,这种方法称为互斥机制。
我们可以通过缓冲通道和 sync.Mutex 来实现这种互斥锁的操作。
2. 缓冲通道实现互斥锁
我们可以通过容量为 1 的缓冲通道来实现互斥锁的操作,保证同一时间只有一个 goroutine 访问同一个变量,下面是修改后的代码:
var sema = make(chan struct{}, 1)var balance intfunc AddFunc() { sema
页:
[1]