Overview

包链接🔗

sync包提供基本的同步原语,例如互斥锁。

除了Once和WaitGroup类型外,大多数都是供低级库例程使用的。

更高层次的同步最好通过channels和通信来完成。

从代码看,sync提供了几种类型:

  • Cond:条件变量
  • Locker:锁的接口定义
  • Map:协程并发安全的Map
  • Mutex:互斥锁
  • Once:单次执行
  • Pool:池
  • RWMutex:读写锁
  • WaitGroup:等待组

几个类型分别对应不同的使用场景。

sync.Cond

Cond实现了一个条件变量,是等待或宣布一个事件发生的goroutines的汇合点。

通俗的说,sync.Cond用来协调那些访问共享资源的goroutine,当共享资源发生变化时,通知被阻塞goroutine。

sync.Cond 经常用在多个 goroutine 等待一个 goroutine 通知(事件发生)的场景。

sync.Map

Map就像Go中的map[interface{}]interface{},但对于多个goroutine的并发使用是安全的,不需要额外的锁或协调。

使用map + sync.Mutex或者sync.RWMutex的方式也可以实现与sync.Map类似的功能,但是在某些场景下,sync.Map具有更高的性能:

Map类型针对两种常见用例进行了优化:

  • 当给定key的条目仅被写入一次却被读取多次时,例如在仅增长的高速缓存中
  • 当多个goroutine读取,写入和覆盖的key都不相关时

在这两种情况下,与与单独的Mutex或RWMutex + map 相比,使用Map可以显着减少锁争用。

sync.Mutex

Mutex是一个相互排斥的锁。Mutex的零值是一个解锁的Mutex。

当调用Lock方法进行加锁时,如果锁已在使用中,则goroutine会阻塞,直到锁可用为止。 当调用UnLock方法进行解锁时,如果锁没有在使用,则会出现运行时错误。

锁定的互斥锁与特定的goroutine没有关联。允许一个goroutine锁定Mutex,然后安排另一个goroutine对其进行解锁。

sync.RWMutex

RWMutex是一个读写器相互排斥的锁。 该锁可以由任意数量的读者或单一的写者持有。RWMutex的零值是一个解锁的mutex。

读读不互斥,读写互斥,写写互斥。

sync.Once

Once的Do(f)方法保证只运行一次,即使f发生panic。 这常用在单例模式,配置文件加载,初始化这些场景下。

sync.Pool

Pool是一组可以单独保存和检索的临时对象。 储存在池子里的任何对象都可能在任何时候被自动删除,而无需通知。 池可以安全地同时被多个goroutine使用。

Pool的作用是缓存已分配但未使用的项目,以便以后再使用,减轻垃圾收集器的压力。也就是说,它使建立高效、线程安全的自由列表变得容易。

池的一个适当的用途是管理一组临时项目,这些临时项目在包的独立客户端之间默默地共享,并可能被重复使用。Pool提供了一种在许多客户端之间分摊分配开销的方法。

当然,Pool并不适用于一些短命的对象池化。

相当于拿出来,做操作,再放回去,操作过的东西放回去的时候是啥样,拿出来的时候就是啥样的。也就是说,拿出来用的时候需要初始化数据或者清空。 如Gin的源码:https://github.com/gin-gonic/gin/blob/v1.7.1/gin.go#L439

// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    // get Context from pool
	c := engine.pool.Get().(*Context)
    // reset
	c.writermem.reset(w)
	c.Request = req
	c.reset()

    // use c
	engine.handleHTTPRequest(c)

    // put back
	engine.pool.Put(c)
}

sync.WaitGroup

一个WaitGroup等待一个goroutine的集合完成。 主的goroutine调用Add来设置要等待的goroutine的数量。 然后每个goroutine运行,完成后调用Done。

同时,可以用Wait来阻塞,直到所有的goroutine都完成。

更新于2021/05/12 23:45