Go的三种扩展原语之 — Semaphore

2023年 7月 22日 37.3k 0

概述

信号量是并发编程中常见的一种同步机制,在需要控制访问资源的进程数量时就会用到信号量,它会保证持有的计数器在 0 到初始化的权重之间波动。

  • 每次获取的资源都会将信号量中的计数器减去对应的数值,在释放时重新加回来
  • 当遇到计数器大于信号量大小时,会进入休眠等待其他线程释放信号

Go语言的扩展包中提供了带权重的信号量 semaphore.Weighted,我们可以按照不同的权重管理资源的访问,这种结构体暴露了 4 个方法:

  • semaphore.NewWeighted —— 用于创建新的信号量
  • semaphore.Weighted.Acquire —— 阻塞地获取指定权重的资源,如果当前没有空闲资源,会陷入休眠等待
  • semaphore.Weighted.TryAcquire —— 非阻塞地获取指定权重的资源,如果当前没有空闲资源,会直接返回 false
  • semaphore.Weighted.Relesae —— 用于释放指定权重的资源

结构体

semaphore.NewWeighted 方法能提供传入的大量权重创建一个指向 semaphore.Weighted 结构体的指针:

func NewWeighted(n int64) *Weighted {
    w := &Weighted{size: n}
    return w
}

type Weighted struct {
    size    int64
    cur     int64
    mu      sync.Mutex
    waiters list.list
}

semaphore.Weighted 结构体中包含一个 waiters 列表,其中存储着等待获取资源的 Goroutine。除此之外,它还包含当前信号量的上限以及一个计数器 cur, 这个计数器的范围就是 [0,size]

信号量中的计数器会随着用户对资源的访问和释放而改变,引入的权重概念能够提供更细粒度的资源访问控制,尽可能满足常见用例。

获取

semaphore.Weighted.Acquire 方法能用于获取指定权重的资源,其中包含 3 中情况:

  • 当信号量中剩余资源大于获取的资源并且没有等待的 Goroutine 时,会直接获取信号量
  • 当需要获取的信号量大于 semaphore.Weighted 的上限时,由于不可能满足条件,因此会直接返回错误
  • 遇到其他情况时,会将当前 Goroutine 加入等待列表,并通过 select 等待调度器唤醒当前 Goroutine,Goroutine 被唤醒后会获取信号量
func (s *Weighted) Acquire(ctx context.Context, n int64) error {
if s.size - s.cur >= n && len(s.waiters) == 0 {
s.cur += n
return nil
}

...

ready := make(chan struct{})
w := waiter{n: n, ready: ready}
elem := s.waiters.PushBack(w)
select {
case

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论