在golang的web服务中,有没有必要限制goroutine的数量?

标题描述的不够清楚。
使用golang创建传统的web服务,通过路由访问后端接口,在该接口中有较多的数据库查询操作,举个例子:

func Test() {
    go func() {
        // 查询1
    }()
    
    go func() {
        // 查询2
    }()
    
    go func() {
        // 查询3
    }()
}

将查询(每个查询都相互独立,没有依赖)放到多个goroutine中,理论上是能够提高这个接口的响应速度的。那么问题来了,在高并发的情况下,会创建大量的goroutine, 在后端是否有必要去做一个限制goroutine数量的操作?如何优雅的实现这样的操作。我大概看了一下网上的文章:

type Grpool struct {
    queue chan uint8
    wg *sync.WaitGroup
}

func (gp *Grpool) Add(n int)  {
    for i := 0; i < n; i++ {
        gp.queue <- uint8(1)
    }
    gp.wg.Add(n)
}

func (gp *Grpool) Done()  {
    <- gp.queue
    gp.wg.Done()
}

func (gp *Grpool) Wait()  {
    gp.wg.Wait()
}

通过上面的结构来实现阻塞wg.Add的操作。但问题是我构建了一个全局单例的Grpool对象:pool, 并且在下面的代码中进行使用:

func Test() {
    pool.Add(3)
    
    go func() {
        // 查询1
        pool.Done()
    }()
    
    go func() {
        // 查询2
        pool.Done()
    }()
    
    go func() {
        // 查询3
        pool.Done()
    }()
    
    pool.Wait()
}

但是,在pool.Wait()的时候是有问题的,因为pool是全局单例的,当有别的用户调用该接口的时候,pool.Wait可能会一直处于阻塞状态

阅读 5.3k
1 个回答

首先, 我觉得要不要限制 goroutine 数量得看瓶颈或关键问题点.

其次, 全局单例 Grpool 很有问题, 以下是 sync.WaitGroup 官方文档

func (wg *WaitGroup) Add(delta int)

Add adds delta, which may be negative, to the WaitGroup counter.
If the counter becomes zero, all goroutines blocked on Wait are released.
If the counter goes negative, Add panics. 

这意味着时间相近的多个请求和可能会绑在一起, 变成多个 pool.Wait() 同时返回.

以下是资源池的简单示例, 希望有益

package main

type Pool struct {
    q chan int
}

func NewPool(max_size int) *Pool {
    return &Pool{q: make(chan int, max_size)}
}

func (p *Pool) Acquire() {
    p.q <- 1
}

func (p *Pool) Release() {
    <-p.q
}

func main() {
    p := NewPool(10)
    p.Acquire()
    // ...
    p.Release()
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题