Go 限制并发、限速
Go的并发使用起来非常容易,随意就起个goroutine。但是日常开发中,考虑到机器CPU、内存、文件描述符、网络以及下游中间件性能等等的影响,通常不会无脑使用并发。
buffered channel如果满了,再塞入则会阻塞,直到有释放才行。下面例子,就是利用这个机制。
限制并发1
这个例子展示了,同一时刻,确保最多只有一定数量的goroutine运行。
var wg sync.WaitGroup
const MAX_NUM = 5
func main() {
  var works []string
  for i := 'a'; i < 'a'+26; i++ {
    works = append(works, string(i))
  }
  // 一个容量是 MAX_NUM 的channel
  restricted := make(chan struct{}, MAX_NUM)
  wg.Add(len(works))
  for _, w := range works {
    go doIt(restricted, w)
  }
  wg.Wait()
}
func doIt(ch chan struct{}, url string) {
  // 进入工作函数后,向通道塞入一个元素,如果是满的则会阻塞,直到有位置。
  // 保证同一时刻最多只有 MAX_NUM 个goroutine运行
  ch <- struct{}{}
  // 结束后释放
  defer func() {
    <-ch
  }()
  // 工作
  time.Sleep(time.Second)
  fmt.Printf("do :%s\n", url)
  wg.Done()
}
限制并发2
这个例子展示,m个goroutine干n个活儿。
const (
  goroutinesNum = 5
  tasksNum = 20
)
var wg sync.WaitGroup
func init() {
  rand.Seed(time.Now().Unix())
}
func main() {
  worksCh := make(chan string, tasksNum)
  // 模拟n个活儿
  for i := 0; i < tasksNum; i++ {
    worksCh <- fmt.Sprintf("work %d", i)
  }
  // todo close channel
  close(worksCh)
  wg.Add(goroutinesNum)
  for i := 0; i < goroutinesNum; i++ {
    go doSth(i, worksCh)
  }
  wg.Wait()
}
func doSth(num int, worksCh chan string) {
  defer wg.Done()
  for w := range worksCh {
    // 干活
    time.Sleep(time.Millisecond * time.Duration(rand.Int63n(1000)))
    fmt.Printf("goNo. %d, done %s\n", num, w)
  }
}