Go中sync包学习
前面刚讲到goroutine和channel,通过goroutine启动一个协程,通过channel的方式在多个goroutine中传递消息来保证并发安全。今天我们来学习sync包,这个包是Go提供的基础包,提供了锁的支持。但是Go官方给的建议是:不要以共享内存的方式来通信,而是要以通信的手段来共享内存。所以他们是提倡使用channel的方式来实现并发控制。
学过Java的同学对锁的概念肯定不陌生,在Java中提供Sychronized
关键字提供独占锁,Lock
类提供读写锁。在sync包中实现的功能也是与锁相关,包中主要包含的对象有:
- Locker:提供了加锁和解锁的接口
- Cond:条件等待通过 Wait 让例程等待,通过 Signal 让一个等待的例程继续,通过 Broadcast 让所有等待的例程继续。
- Map:线程安全的map ,同时被多个goroutines调用是安全的。
- Mutex:互斥锁,用来保证在任一时刻,只能有一个例程访问某对象。实现了Locker接口。Mutex 的初始值为解锁状态,Mutex 通常作为其它结构体的匿名字段使用,使该结构体具有 Lock 和 Unlock 方法
- Once:Once 是一个可以被多次调用但是只执行一次,若每次调用Do时传入参数f不同,但是只有第一个才会被执行。
- Pool:用于存储临时对象,它将使用完毕的对象存入对象池中,在需要的时候取出来重复使用,其中存放的临时对象随时可能被 GC 回收掉如果该对象不再被其它变量引用
- RWMutex:读写互斥锁,RWMutex 比 Mutex 多了一个“读锁定”和“读解锁”,可以让多个例程同时读取某对象。RWMutex 的初始值为解锁状态。RWMutex 通常作为其它结构体的匿名字段使用。
- WaitGroup :用于等待一组例程的结束。主例程在创建每个子例程的时候先调用 Add 增加等待计数,每个子例程在结束时调用 Done 减少例程计数。之后主例程通过 Wait 方法开始等待,直到计数器归零才继续执行。
Once.Do(),向 Do 传入一个函数,这个函数在第一次执行 Once.Do() 的时候会被调用,以后再执行 Once.Do() 将没有任何动作,即使传入了其它的函数,也不会被执行,如果要执行其它函数,需要重新创建一个 Once 对象。
看一个很简单的例子:
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
onceBody := func() {
fmt.Println("我只会出现一次")
}
done := make(chan bool)
for i := 0; i < 3; i++ {
go func() {
once.Do(onceBody)
done <- true
}()
}
for i := 0; i < 3; i++ {
<-done
}
}