这里是讲sync整个包的使用,但是会排出sync.pool的使用,sync.pool为啥单独拿出来讲,是因为sync.pool在高并发以及重复利用的变量使用中有很好的效果。sync包是业务中最常用的类库能排名前三,学会sync包的使用是很有必要的。
下面我们看一下sync包中sync,WaitGroup其他功能。
sync.WaitGroup:作为多协程之间同步的一种机制,用于等待一组线程的结束。
主线程调用Add方法来设定应等待的线程的数量,同时主线程里可以调用Wait方法阻塞至所有线程结束。
每个被等待的子线程在结束时应调用Done方法。下面我们来看一个例子:
package main
import (
"fmt"
"sync"
"time"
"math/rand"
)
func main() {
//这里是初始化
wg := new(sync.WaitGroup)
//这里是添加可能出现的协程数,必须在主线程执行。
wg.Add(4)
//这里使用go func 表示协程调用
go test(wg)
go test(wg)
go test(wg)
go test(wg)
//这里是主线程wg组等待
wg.Wait()
fmt.Println("over")
}
func test(w *sync.WaitGroup) {
time.Sleep(time.Second * 2)
fmt.Println(time.Now())
fmt.Println(rand.Int())
w.Done()
}看一下返回结果:
2019-11-23 14:38:46.1110006 +0800 CST m=+2.013265501
5577006791947779410
2019-11-23 14:38:46.1110006 +0800 CST m=+2.013265501
8674665223082153551
2019-11-23 14:38:46.1110006 +0800 CST m=+2.013265501
6129484611666145821
2019-11-23 14:38:46.1110006 +0800 CST m=+2.013265501
4037200794235010051
over下面我们看一下Mutex(互斥锁)和RWMutex(读写锁)的使用
下面的代码的程序主要是通过多个协程并发incr和decr让变量c按照顺序增加和减少,来验证互斥锁的使用。
package main
import (
"sync"
"fmt"
"time"
)
var l = new(sync.Mutex)
func main() {
//首先看一下Mutex(互斥锁)和RWMutex(读写锁)
//初始化c
c := 0
//增加一个全局锁
lock := new(sync.Mutex)
incr := func() {
lock.Lock() // 1
defer lock.Unlock() // 2
c++
fmt.Printf("incr: %d\n", c)
}
decr := func() {
lock.Lock() // 1
defer lock.Unlock() // 2
c--
fmt.Printf("decr: %d\n", c)
}
//用waitgroup 让主线程等待子线程的结果返回,多个协程调用incr或者decr方法来对变量c进行增加和减少。
w := new(sync.WaitGroup)
for i := 0; i <= 3; i++ {
w.Add(1)
go func() {
defer w.Done()
incr()
}()
}
for i := 0; i <= 3; i++ {
w.Add(1)
go func() {
defer w.Done()
decr()
}()
}
w.Wait()
}下面这个是结果,结果是按照每次只增加1或者减少1,表示互斥锁的使用得到验证。实际业务中我们主要是map,contain/list等线程不安全的数据类型才使用上锁。
incr: 1
decr: 0
decr: -1
decr: -2
decr: -3
incr: -2
incr: -1
incr: 0