4. sync
sync 包提供了基本的同步原语,如互斥锁。除了 Once 和 WaitGroup 之外,大部分都是适用于低水平程序线程的,高水平的同步使用 channel 通常更好。
4.1 Mutex
Mutex 是互斥锁,Lock() 加锁,Unlock() 解锁。如果在 Lock() 之后没有对 Mutex 解锁,那么之后的操作都会被阻塞。
var mu sync.Mutex
func main() {
mu.Lock()
defer mu.Unlock()
}
4.2 RWMutex
RWMutex 是读写锁,分别有 Lock()、Unlock()、RLock()、RUnlock() 四个方法。R 是读,W 是写,加了 R 的锁可以进行读操作,加了 W 的锁可以进行写操作,但是加了 W 的锁后,其他 goroutine 既不能进行读操作也不能进行写操作。
var mu sync.RWMutex
func main() {
mu.RLock()
defer mu.RUnlock()
}
4.3 WaitGroup
WaitGroup 用于等待一组 goroutine 结束。程序控制会一直阻塞,直到这些指定的 goroutine 全部结束。
var wg sync.WaitGroup
func main() {
wg.Add(2)
go func() {
fmt.Println("Goroutine 1")
wg.Done()
}()
go func() {
fmt.Println("Goroutine 2")
wg.Done()
}()
wg.Wait()
}
4.4 Once
Once 用于多次调用同一个函数,但只有第一次调用时才执行函数。
var once sync.Once
func main() {
once.Do(func() {
fmt.Println("Only once")
})
}
4.5 Pool
Pool 用于存储临时对象,它可以安全地被多个 goroutine 并发使用。
var pool = sync.Pool{
New: func() interface{} {
return 0
},
}
func main() {
pool.Put(1)
pool.Put(2)
pool.Put(3)
fmt.Println(pool.Get())
fmt.Println(pool.Get())
fmt.Println(pool.Get())
}
5. context
context 包提供了一个用于跟踪请求的上下文(context)。在 Go 语言中,每个请求都是在独立的 goroutine 中处理的,这就使得我们可以很容易地对每个请求进行并发控制。但是,当我们需要跨越多个 goroutine 时,就需要一种跟踪上下文的机制,这就是 context 包的用武之地。
5.1 context 的声明
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
5.2 context 的应用场景
- 超时控制
- 多个 goroutine 之间的数据传递
- 多个 goroutine 之间的信号通知
6. sync.Map
sync.Map 是 Go 语言提供的内置并发安全的 map,开箱即用。不过需要注意的是,sync.Map 不能使用 map 的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用。
6.1 sync.Map 的声明
type Map struct {
// 包含隐藏或非导出字段
}
6.2 sync.Map 的初始化
var m = sync.Map{}
6.3 sync.Map 的操作
m.Store(key, value) // 设置键值对
m.Load(key) // 获取键值对
m.Delete(key) // 删除键值对
m.Range(func(key, value interface{}) bool {
// 遍历所有键值对
return true
})
6.4 sync.Map 的注意事项
- sync.Map 不能使用 map 的方式进行取值和设置等操作,而是使用 sync.Map 的方法进行调用。
- sync.Map 无法在创建后再进行复制操作。
- sync.Map 与其他结构不同,它不是一个结构体,因此我们在声明的时候只需要声明变量即可,不需要初始化。