大家好,今天将梳理出的 Go语言并发知识内容,分享给大家。 请多多指教,谢谢。

sync.Condsync.Oncesync.Pool

本章节内容

  • sync.Cond
  • sync.Once
  • sync.Pool

sync.Cond

条件变量

Cond 类型原型

Cond 实现了一个条件变量,用于等待或宣布事件发生时 goroutine 的交汇点。在这个定义中,“事件”是指两个或更多的goroutine之间的任何信号,仅指事件发生了,不包含其他任何信息。 通常,你可能想要在收到某个 goroutine 信号前令其处于等待状态。

MutexRWMutexWait()
NewCond()
func (*Cond) Broadcast()
func (*Cond) Signal()
func (*Cond) Wait()
Wait()Singnal()Broadcast()

Cond 使用

举例1:假设我们有一个固定长度为2的队列,并且我们要将10个元素放入队列中。希望一有空间就能放入,所以在队列中有空间时需要立即通知。

输出

main() goroutine
Broadcast()Broadcast

输出

sync.Cond

sync.Once

一次性执行

Once 类型原型

Once 主要作用是只执行一次处理,在第一次使用后将不可复制。

func (o *Once) Do(f func())

Once 使用

输出

sync.Once

sync.Pool

临时对象池

Pool 类型原型

Pool 是一组可以单独保存和检索的临时对象。存储在池中的任何项目可以在任何时候自动删除而不通知。如果发生这种情况时Pool持有唯一的引用,则可能会释放该项。

Pool 可以安全的被多个 goroutine 同时使用。

Pool 的目的是缓存已分配但未使用的项,以便稍后重用,减轻垃圾收集器的压力。也就是说,它使构建高效的、线程安全的空闲列表变得容易。然而,它并不适用于所有的免费列表。

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

sync.Pool
一个很好 Pool 的例子是在 fmt 包中,它维护一个动态大小的临时输出缓冲区存储。存储在负载下伸缩(当许多goroutines正在积极地打印时),在静默时收缩。

注意,作为生存期较短的对象一部分维护的空闲列表不适合用于Pool,因为在这种情况下,开销不会很好地摊销。让这些对象实现它们自己的空闲列表会更有效率。

func (p *Pool) Get() anyGet()Get()Put()Get()
p.Newp.New
func (p *Pool) Put(x any)

Pool 使用

举例1:Pool的主要接口是它的Get方法。 被调用时,Get将首先检查池中是否有可用实例返回给调用者,如果没有,则创建一个新成员变量。使用完成后,调用者调用Put将正在使用的实例放回池中供其他进程使用。

输出

  1. 调用 Get 方法,将在池中定义 New 函数,因为实例尚未实例化
  2. 将先前检索到的实例放回池中,这时实例的可用数量为1个
  3. 执行此调用时,将重新使用先前分配的实例。 New函数不会被调用

举例2:指定分配内存

输出

  1. 储存了字节切片的指针
  2. 指向了字节切片的指针
sync.Poolsync.Pool

举例3:Pool 另一种常见情况是预热分配对象缓存,用于必须尽快运行的操作。通过预先加载获取对另一个对象的引用来减少消费者的时间消耗。

正如这个例子所展现的,池模式非常适合于这种需要并发进程,或者构建这些对象可能会对内存产生负面影响的应用程序。

但是,在确定是否应该使用池时有一点需要注意:如果使用池子里东西在内存上不是大致均匀的,则会花更多时间将从池中检索,这比首先实例化它要耗费更多的资源。

因此,在使用 Pool 时,请记住以下几点:

sync.Pool

Pool 特性

最后总结下临时对象池的特性:

Get()Put()

技术文章持续更新,请大家多多关注呀~~

搜索微信公众号,关注我【 帽儿山的枪手 】



书籍[1] sync标准库[2]

参考资料

[1] Go并发编程实战、Concurrency in Go: .

[2] sync标准库: