go中的sync包

本文介绍了Go语言中的sync包,包括Mutex互斥锁用于保护共享资源的串行访问,RWMutex读写锁允许并发读取而限制写入,WaitGroup用于等待一组goroutine执行完毕,以及并发安全的Map避免数据竞争。通过示例展示了这些工具的使用方法和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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串行执行

image-20210413164504853

执行结果

image-20210413164541448

使用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() 

image-20210413165411852

结果

image-20210413165428072

在写锁没有被获取时,所有的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执行结束后统一返回结果的情况。

image-20210413170559135

结果

image-20210413170613741

主协程在执行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)

image-20210413171612822

结果

image-20210413171633273

在go addNumber这里执行并发,可以看到goroutine1执行0处放0值,1处放1,2处放2。执行3次,goroutine2执行10处放10,11处放11,12处放12,依次执行协程3,4,5。一共放了15个数,所以size就是15。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值