前言

开发过程中,如果不限制并发数,如下代码这种,可能直接造成服务器宕机,而且很多结果不会输出

[!TIP|style:flat]

很多结果不会输出,是因为主协程结束时,子协程也会终止掉。

  1. func main() {
  2. userCount := math.MaxInt64
  3. for i := 0; i < userCount; i++ {
  4. go func(i int) {
  5. // 做一些各种各样的业务逻辑处理
  6. fmt.Printf("go func: %d\n", i)
  7. time.Sleep(time.Second)
  8. }(i)
  9. }
  10. }

尝试chan

  1. package main
  2. import (
  3. "fmt"
  4. "math"
  5. "time"
  6. )
  7. func out(i int, semaphore chan bool){
  8. fmt.Printf("go func: %d\n", i)
  9. // 释放通道
  10. <- semaphore
  11. time.Sleep(time.Second)
  12. }
  13. func main() {
  14. semaphore := make(chan bool, 2)
  15. userCount := math.MaxInt8
  16. for i := 0; i < userCount; i++ {
  17. // 占用通道
  18. semaphore <- true
  19. go out(i, semaphore)
  20. }
  21. }

确实可以2个协程并发,但是和上面结果一样,很多结果不会输出,是因为主协程结束时,子协程也会终止掉。

  1. go func: 1
  2. go func: 0
  3. go func: 3
  4. go func: 4
  5. go func: 5
  6. go func: 6
  7. go func: 7
  8. go func: 8

尝试sync

主要使用sync.WaitGroup{}

  1. package main
  2. import (
  3. "fmt"
  4. "math"
  5. "sync"
  6. "time"
  7. )
  8. var wg = sync.WaitGroup{}
  9. func out(i int){
  10. fmt.Printf("go func: %d\n", i)
  11. time.Sleep(time.Second)
  12. wg.Done()
  13. }
  14. func main() {
  15. userCount := math.MaxInt8
  16. for i := 0; i < userCount; i++ {
  17. wg.Add(1)
  18. go out(i)
  19. }
  20. // 等待全部执行完
  21. wg.Wait()
  22. }

所有结果都显示出来了,也就是说所有子协程都执行完了,但是没有控制并发数量

尝试chan+sync√

从上面2个可以看出,一个可以控制并发数量,另一个可以让所有子协程都执行完,所以结合一下,就能达到我们的目的了

  1. package main
  2. import (
  3. "fmt"
  4. "math"
  5. "sync"
  6. "time"
  7. )
  8. var wg = sync.WaitGroup{}
  9. func out(i int, semaphone chan bool){
  10. fmt.Printf("go func: %d\n", i)
  11. time.Sleep(time.Second)
  12. // 释放通道
  13. <- semaphone
  14. defer wg.Done()
  15. }
  16. func main() {
  17. semaphone := make(chan bool, 2)
  18. userCount := math.MaxInt8
  19. for i := 0; i < userCount; i++ {
  20. wg.Add(1)
  21. // 占用通道
  22. semaphone <- true
  23. go out(i, semaphone)
  24. }
  25. // 等待全部执行完
  26. wg.Wait()
  27. }

结合一下,确实能达到我们想到的效果了!!!就是结果有点乱,一般来说不影响了

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

信号量Semaphore

和Python中的信号量一样,感觉是结合了chan+sync,确实是一个很好的方案,输出的结果也是按顺序输出的

import ( “fmt” “github.com/EDDYCJY/gsema” “math” “time” )

var semaphore = gsema.NewSemaphore(2)

func out(i int){ fmt.Printf(“go func: %d\n”, i) time.Sleep(time.Second) defer semaphore.Done() }

func main() { userCount := math.MaxInt8 for i := 0; i < userCount; i++ { semaphore.Add(1) go out(i) } semaphore.Wait() }

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

协程池

这个就是一次性创建所有的协程,然后再根据大小来调用