Go 如何控制并发的goroutine数量?

参考解析

为什么要控制goroutine并发的数量?

在开发过程中,如果不对goroutine加以控制而进行滥用的话,可能会导致服务整体崩溃。比如耗尽系统资源导致程序崩溃,或者CPU使用率过高导致系统忙不过来。

用什么方法控制goroutine并发的数量?

有缓冲channel

利用缓冲满时发送阻塞的特性

  1. package main
  2. import (
  3. "fmt"
  4. "runtime"
  5. "time"
  6. )
  7. var wg = sync.WaitGroup{}
  8. func main() {
  9. // 模拟用户请求数量
  10. requestCount := 10
  11. fmt.Println("goroutine_num", runtime.NumGoroutine())
  12. // 管道长度即最大并发数
  13. ch := make(chan bool, 3)
  14. for i := 0; i < requestCount; i++ {
  15. wg.Add(1)
  16. ch <- true
  17. go Read(ch, i)
  18. }
  19. wg.Wait()
  20. }
  21. func Read(ch chan bool, i int) {
  22. fmt.Printf("goroutine_num: %d, go func: %d
  23. ", runtime.NumGoroutine(), i)
  24. <-ch
  25. wg.Done()
  26. }

输出结果:默认最多不超过3(4-1)个goroutine并发执行

  1. goroutine_num 1
  2. goroutine_num: 4, go func: 1
  3. goroutine_num: 4, go func: 3
  4. goroutine_num: 4, go func: 2
  5. goroutine_num: 4, go func: 0
  6. goroutine_num: 4, go func: 4
  7. goroutine_num: 4, go func: 5
  8. goroutine_num: 4, go func: 6
  9. goroutine_num: 4, go func: 8
  10. goroutine_num: 4, go func: 9
  11. goroutine_num: 4, go func: 7

无缓冲channel

任务发送和执行分离,指定消费者并发协程数

  1. package main
  2. import (
  3. "fmt"
  4. "runtime"
  5. "sync"
  6. )
  7. var wg = sync.WaitGroup{}
  8. func main() {
  9. // 模拟用户请求数量
  10. requestCount := 10
  11. fmt.Println("goroutine_num", runtime.NumGoroutine())
  12. ch := make(chan bool)
  13. for i := 0; i < 3; i++ {
  14. go Read(ch, i)
  15. }
  16. for i := 0; i < requestCount; i++ {
  17. wg.Add(1)
  18. ch <- true
  19. }
  20. wg.Wait()
  21. }
  22. func Read(ch chan bool, i int) {
  23. for _ = range ch {
  24. fmt.Printf("goroutine_num: %d, go func: %d
  25. ", runtime.NumGoroutine(), i)
  26. wg.Done()
  27. }
  28. }