go中的sync包
在Go语言中,除了使用channel进行goroutine之间的通信和同步操作外,还可以使用syne包下的并发工具。
并发工具类 | 说明 |
---|---|
Mutex | 互斥锁 |
RWMutex | 读写锁 |
WaitGroup | 并发等待组 |
Map | 并发安全字典 |
Cond | 同步等待条件 |
Once | 只执行一次 |
Pool | 临时对象池 |
Mutex(互斥锁)
sync. Muer互斥锁能够保证在同一个时间段内仅有一个goroutine持有锁,这就能够保证在某一时间段内有且仅有一个goroutine访问共享资源,其他申请锁的goroutine将会被阻塞直到锁被释放,然后重新争抢锁的持有权。syne.Mutex 提供以下两个方法:
//加锁
Lock()
//释放锁
Unlock()
使用sync.Mutex控制多goroutine串行执行
执行结果
使用sync.Mutex能够保证每次只有一个goroutine访问同步代码块中的资源从而对资源进行原子操作
RWMutex(读写锁)
sync. RWMutex即我们熟知的读写锁,它将读锁和写锁的操作分离开来。为了保证在同一时间段能够有多个goroutine访问同一资源,它需要满足以下条件:
-
在同一时间段只能有一个goroutine获取到写锁。
-
在同一时间段可以有任意多个gorouinte获取到读锁。
-
在同一时间段只能存在写锁或读锁(读锁和写锁互斥)。
sync.RWMutex提供以下接口:
//写加锁
func (rw *RWMutex) Lock()
//写解锁
func (rw *RWMutex) Unlock()
//读加锁
func (rw *RWMutex) RLock()
//读解锁
func (rw *RWMutex) RUnlock()
结果
在写锁没有被获取时,所有的read goroutinue可以同时申请到读锁,比如func1和func以及func3和func2和func4就是同时获取到读锁;申请写锁的goroutine必须等到没有任何读锁和写锁存在时才能申请成功,可以看到他们获取写锁的时间大约相差1s。
WaitGroup(并发等待组)
使用sync.WaitGroup的goroutine会等待预设好数量的goroutine会等待预设好数量的goroutine都提交执行结束后,才会继续往下执行代码,提供接口如下所示:
//添加等待数量,传递负数表示任务减1
func (wg *WaitGroup) Add(delta int)
//等待数量减1
func (wg *WaitGroup) Done()
//使goroutine等待于此
func (wg *WaitGroup) Wait()
在goroutine调用waitGroup.Wait进行等待之前,需要保证waitGroup中等待数量大于1,即waitGroup.Add方法需要在waitGroup.Wait之前执行,否则等待就会被忽略。除此之外,还需要保证waitGroup.Done执行次数与waitGroup.Add添加的等待数量一致,过少会导致等待goroutine死锁,过多会导致程序panic。
sync.WaitGroup适用于执行批量操作,等待所有goroutine执行结束后统一返回结果的情况。
结果
主协程在执行waitGroup.Done后,需要等待waitGroup中的等待数变为0之后才可以继续往后执行。
Map (并发安全字典)
sync. Map即添加了同步控制的字典。Go语言中原生的Map并不是并发安全的,在多个goroutine同时往Map中添加数据时,可能会导致部分添加数据的丢失,为了避免这种情况,提供了syn.Map. 相对于原生的Map,它只提供以下接口:
//根据key获取存储值
func (m *Map) Load(key interface{}) (value interface{}), ok bool)
//设置key-value 对
func (m *Map) store(key,value interface{})
//如果key存在则返回key对应的value,否则设置key-value对
func (m *Map) LoadOrStore(key,value interface{}) (actual interface{},loaded bool)
//删除一个key以及对应的值
func (m *Map) Delete(key interface{})
//无序遍历map
func (m *Map) Range(f func(key,value interface{}) bool)
结果
在go addNumber这里执行并发,可以看到goroutine1执行0处放0值,1处放1,2处放2。执行3次,goroutine2执行10处放10,11处放11,12处放12,依次执行协程3,4,5。一共放了15个数,所以size就是15。