Golang中实现Goroutine等待机制:避免资源池满时返回nil
解决Golang资源池满时goroutine等待可用Page实例的问题
当前资源池实现的核心问题是:当池满且无空闲Page时直接返回nil,不符合“无错误绝不返回nil”的需求。我们可以通过引入sync.Cond条件变量实现阻塞等待逻辑,直到有可用资源或上下文取消。
1. 修改Browse结构体,添加条件变量
条件变量需要与互斥锁配合使用,用于等待和唤醒goroutine:
type Browse struct { b *rod.Browser ctx context.Context pool []*Page launch *Launch maxSize int mu sync.Mutex cond *sync.Cond // 新增:用于等待/唤醒的条件变量 }
注意:在初始化Browse实例时,需要初始化条件变量:
// 示例初始化代码 func NewBrowse(b *rod.Browser, ctx context.Context, maxSize int) *Browse { browse := &Browse{ b: b, ctx: ctx, maxSize: maxSize, } browse.cond = sync.NewCond(&browse.mu) // 绑定互斥锁 return browse }
2. 重写Page方法,实现阻塞等待逻辑
修改后的方法会循环检查资源,无可用资源时阻塞等待,直到被唤醒或上下文取消:
func (b *Browse) Page(proxy string, url string) (*Page, error) { b.mu.Lock() defer b.mu.Unlock() // 循环等待:处理虚假唤醒+重复检查资源状态 for { // 优先复用空闲Page for i := range b.pool { if !b.pool[i].IsActive() { err := b.pool[i].SetUp(proxy) if err != nil { return nil, err } return b.pool[i], nil } } // 池未满则创建新Page if len(b.pool) < b.maxSize { p := stealth.MustPage(b.b) page, err := NewPage(p, proxy, url) if err != nil { return nil, err } b.pool = append(b.pool, page) return page, nil } // 池满且无空闲:等待唤醒,同时监听上下文取消 select { case <-b.ctx.Done(): return nil, b.ctx.Err() default: b.cond.Wait() // 自动释放锁,被唤醒后重新获取锁 } } }
3. 修改PutPage方法,唤醒等待的goroutine
当Page被放回并清理后,需要通知等待队列中的goroutine有资源可用:
func (b *Browse) PutPage(page *Page) { b.mu.Lock() defer func() { b.mu.Unlock() b.cond.Signal() // 唤醒一个等待的goroutine(若用Broadcast则唤醒所有,按需选择) }() err := page.CleanUp() if err != nil { fmt.Println("Error cleaning up page", err) } }
关键逻辑说明
- 虚假唤醒处理:使用
for循环而非if检查资源状态,避免Wait()被虚假唤醒后直接返回无效资源。 - 上下文取消支持:通过
select监听ctx.Done(),防止goroutine无限等待。 - 效率优化:使用
Signal()仅唤醒一个等待的goroutine,比Broadcast()更高效(若无需批量唤醒场景)。
使用方式不变
原有的调用代码无需修改,现在方法在无错误时将始终返回有效的*Page实例:
page, err := v.browse.Page(s.proxy, s.url) if err != nil { fmt.Println("Error getting page", err) return } defer v.browse.PutPage(page)
内容的提问来源于stack exchange,提问作者nerthesa 12
相关产品推荐
相关产品推荐

