sync包

大家好,我是小许,标准库中的sync包在我们的日常开发中用的颇为广泛,那么大家对sync包的用法知道多少呢,这篇文章就大致讲一下sync包和它的使用

Package sync provides basic synchronization primitives such as mutual exclusion locks. Other than the Once and WaitGroup types, most are intended for use by low-level library routines. Higher-level synchronization is better done via channels and communication.
Values containing the types defined in this package should not be copied.

这句话大意就是说:sync包提供了基本的同步基元,如互斥锁。除了Once和WaitGroup类型,大部分都是适用于低水平程序线程,高水平的同步使用channel通信更好一些

包中定义了以下类型: Locker, Once, Mutex, RWMutex, WaitGroup, Pool。接下来我们逐个讲每种类型的使用,所有这些在Go sdk中的src/runtime/sync包,可逐个查看,特别是结合test一起

1.Locker

Locker接口,包含Lock()和Unlock()两个方法,用于代表一个能被加锁和解锁的对象.

Locker接口

1.1 Lock()

Lock方法锁住Mutex,如果Mutex已经加锁,则阻塞直到m解锁


1.2 UnLock()

Unlock方法解锁m,如果解锁一个未加锁的mutex会导致运行时错误、锁定m与特定的groutine无关。允许不同的groutine进行加锁、解锁

2.Once

Once是只执行一次动作的对象,使用后不得复制

Once结构

Once只有一个Do方法

Do方法当且仅当第一次被调用时才执行函数f。once.Do(f)被多次调用,只有第一次调用会执行f,即使f每次调用Do 提供的f值不同。需要给每个要执行仅一次的函数都建立一个Once类型的实例

3.Mutex

Mutex是一个互斥锁,可以创建为其他结构体的字段;零值为解锁状态。Mutex类型的锁和线程无关,可以由不同的线程加锁和解锁。实现了Locker()接口的UnLock()和Locker()方法,同一时刻一段代码只能被一个线程运行

Mutex在大量并发的情况下,会造成锁等待,对性能的影响比较大

Mutex结构

4.RWMutex

Mutex是一个读写互斥锁,该锁可以被同时多个读取者持有或唯一个写入者持有

有以下方法可使用

5.WaitGroup

WaitGroup 对象内部有一个计数器,最初从0开始,它有三个方法:Add(), Done(), Wait() 用来控制计数器的数量。Add(n) 把计数器设置为n ,Done() 每次把计数器-1 ,wait() 会阻塞代码的运行,直到计数器的值减为0.

使用示例

6.Pool

Pool是一个可以分别存取的临时对象的集合,可以被看作是一个存放可重用对象的值的容器、过减少GC来提升性能,是Goroutine并发安全的。有两个方法 Get()、Set()

其实在开发使用中我们间接也是使用到了sync.Pool,比如标准库中的fmt、还有gin、iris框架中的Context

6.1 fmt中sync.Pool

newPrinter就是调用的sync.Pool.Get(),拿到pp指针.首先是做了一些format操作。然后调用free()方法,将使用过得pp放回到ppFree中。归还之前将p的部分字段重置,以保证下次调用的是原始pp

6.2 iris框架中的Context中的sync.Pool

iris.New()创建并返回一个空的 iris *Application实例。New()函数中的context.New(),传入一个func,返回一个context.Pool给到 Application的ContextPool。可以看到传入的func实际是给到了sync.Pool.New。app.ContextPool就是存储上下文变量Context的管理池。

Acquire()获取context, Release()释放对象