本文介绍golang的sync包里的知识点与使用方式。
什么是Sync包
Golang sync包提供了基础的异步操作方法,包括互斥锁Mutex,执行一次Once和并发等待组WaitGroup
主要的功能如下:
1 | sync.Mutex: 互斥锁 |
sync.Mutex&sync.RWMutex
- sync.Mutex称为互斥锁,常用在并发编程里面。互斥锁需要保证的是同一个时间段内不能有多个并发协程同时访问某一个资源(临界区)
- sync.RWMutex目的是为了能够支持多个并发协程同时读取某一个资源,但只有一个并发协程能够更新资源。也就是说读和写是互斥的,写和写也是互斥的,读和读是不互斥的。
这两个锁的用法在之前已介绍过,故不再重复介绍。需要的请查看:Golang代码优化04-锁
sync.WaitGroup
sync.WaitGroup指的是等待组,在Golang并发编程里面非常常见,指的是等待一组工作完成后,再进行下一组工作。
1 | func (wg *WaitGroup) Add(delta int) Add添加n个并发协程 |
简单的理解就是Boss监督Worker工作,等待所有的Worker完成后才收工!
1 | func main() { |
sync.Once
sync.Once指的是只执行一次的对象实现,常用来控制某些函数只能被调用一次。
插播一条故事,关于sync.Once,在03-31与EasySwoole的作者有过一次讨论
他:说说sync.Once的作用
我:常用于单例模式
他:一看你就是Go的初学者。
我:恩,大佬说说看,我好学学一波
他:本质在于、、、、go是多线程协程,而单例模式的时候,如果用锁来解决单例问题,那么效率是非常差的。这个可以参考java多线程下的同步锁问题,为此go提供了一个sync.Once机制,不过实际上,go的本质实现,也是锁。嗯,反正就是,你没回到到问题的本质。
我:学习了,受益颇多。
emmm…于是乎抱着听了大佬的一波解释,因而有点怀疑的想法,下来仔细研究了sync.Once的源码,代码其实很简单,和大佬说的其实差不多。但是实际不是那么简单,写go的人真的牛逼(实在是666)。下面看看源码:
1 | type Once struct { |
总的来说,sync.Once的使用场景例如单例模式、系统初始化。
因为这个插曲,深入研究了sync.Once的原理,受益匪浅,还是感谢大佬的启发。虽然大佬说的有道理,但我依然坚持我的观点
常用于单例模式
。如果有一天谁能说服我不能这么用,我也会听听理由,自己思考一番。
sync.Cond
sync.Cond指的是同步条件变量,一般需要与互斥锁组合使用,本质上是一些正在等待某个条件的协程的同步机制。主要函数如下:
1 | // Wait 等待通知 |
- 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23func main() {
cond := sync.NewCond(&sync.Mutex{})
for i := 0; i < 3; i++ {
go func(i int) {
cond.L.Lock() // 获取锁
fmt.Println("上班,摸鱼...")
cond.Wait() //等待通知 暂时阻塞
fmt.Println("等待下班...")
cond.L.Unlock() //释放锁
fmt.Println("下班,Worker:", i)
}(i)
}
time.Sleep(1e9)
cond.Signal() // 让leader先走
time.Sleep(1e9)
cond.Broadcast() // 全部下班
time.Sleep(2e9) // 等待协程执行完成
}
sync.Pool
通常用golang来构建高并发场景下的应用,但是由于golang内建的GC机制会影响应用的性能,为了减少GC,golang提供了对象重用的机制,也就是sync.Pool对象池
注:千万不能把它当成内存池使用。
1 | func main() { |
- 接收并读取http的响应内容
实际代码之前已描述过,请查看:Golang代码优化03-Http响应处理
sync.Map
貌似不建议使用了。暂不介绍
- 本文作者: Hongker
- 本文链接: https://hongker.github.io/2020/04/01/golang-sync/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!