• golang - 控制协程并发数的3种方法


    在golang中使用协程非常方便,如果有大量任务要处理,且任务间没有关联,可以并行同时处理的话,就非常适合用golang的协程处理。串行一个个执行需要的效率,远没有并行同时处理来的快,特别是当处理每个任务需要的时间越长,使用并行效果就越明显。


    是的,golang就是利用多核cpu的云时代语言。


    有些时候,golang起的协程特别多的话,机器的性能或其他服务组件会扛不住,比如服务器IO,数据库连接等,这时候需要主动控制协程并发数,避免服务崩溃。
    下面是golang控制协程并发数的几种方法,很有意思。


    代码实践:
    方法一:使用有缓冲容量长度的channel控制

    1. package main
    2. import (
    3. "fmt"
    4. "time"
    5. )
    6. //同时最多10个协程运行
    7. var limitMaxNum = 10
    8. var chData = make(chan int, limitMaxNum)
    9. //100个任务要处理
    10. var tasknum = 100
    11. //使用 有缓冲容量长度的channel
    12. func main() {
    13. var i, j int
    14. var chanRet = make(chan int, tasknum) //运行结果存储到chanRet
    15. //运行处理
    16. go func() {
    17. for i = 0; i < tasknum; i++ {
    18. chData <- 1
    19. go dotask(i, chanRet)
    20. }
    21. }()
    22. //获取返回结果
    23. for j = 0; j < tasknum; j++ {
    24. <-chData
    25. <-chanRet
    26. // fmt.Println("ret:", ret)
    27. }
    28. fmt.Println("main over")
    29. }
    30. func dotask(taskid int, chanRet chan int) {
    31. time.Sleep(time.Millisecond * 100)
    32. fmt.Println("finish task ", taskid)
    33. chanRet <- taskid * taskid
    34. }


    应用场景:适合知道任务数量,最简单的使用方式。



    方法2:使用channel+waitGroup

    1. package main
    2. import (
    3. "fmt"
    4. "sync"
    5. "time"
    6. )
    7. var limitMaxNum = 10
    8. var chData = make(chan int, limitMaxNum)
    9. var jobGroup sync.WaitGroup
    10. var tasknum = 100
    11. //使用 有缓冲容量长度的channel
    12. func main() {
    13. var i int
    14. //var chanRet = make(chan int, tasknum)
    15. //处理任务,最多同时有10个协程
    16. for i = 0; i < tasknum; i++ {
    17. chData <- 1
    18. go dotask(i)
    19. }
    20. //使用Wait等待所有任务执行完毕
    21. jobGroup.Wait()
    22. fmt.Println("main over")
    23. }
    24. func dotask(taskid int) {
    25. jobGroup.Add(1)
    26. time.Sleep(time.Millisecond * 100)
    27. fmt.Println("finish task ", taskid)
    28. // fmt.taskid * taskid
    29. <-chData
    30. jobGroup.Done()
    31. }


    应用场景:waitGroup开箱即用,不管任务数量是否提前清楚的情况下,都可以用,也很简单,少了次循环。


    方法3:优雅使用waitGroup+channel+range

    1. package main
    2. import (
    3. "fmt"
    4. "sync"
    5. "time"
    6. )
    7. var limitMaxNum = 10
    8. var chData = make(chan int, limitMaxNum)
    9. var jobGroup sync.WaitGroup
    10. var tasknum = 100
    11. func main() {
    12. var i int
    13. var j int
    14. //组装任务
    15. chanTask := make(chan int, tasknum)
    16. for j = 0; j < tasknum; j++ {
    17. chanTask <- j
    18. }
    19. close(chanTask)
    20. jobGroup.Add(tasknum)
    21. for i = 0; i < limitMaxNum; i++ { //最多10个协程
    22. go dotask3(chanTask)
    23. }
    24. jobGroup.Wait()
    25. fmt.Println("main over")
    26. }
    27. func dotask3(taskChan chan int) {
    28. for taskid := range taskChan { //每个协程拼命抢夺任务,直到任务完结
    29. time.Sleep(time.Millisecond * 500)
    30. fmt.Println("finish task ", taskid)
    31. jobGroup.Done()
    32. }
    33. }


    应用场景:这个处理方式最优雅,不仅控制了同时运行的只有10个协程,而且整个运行过程中只起了10个协程(当然不包括主协程),处理方式也很优雅,高效利用了channel 是否阻塞特点,最推荐这个。

  • 相关阅读:
    Android混合式开发框架搭建集成Flutter3.0.1
    tomcat面试和Spring的面试题
    NMS(openCV API)
    Spring MVC拦截器、文件上传和全局异常处理
    448. 找到所有数组中消失的数字
    Unity 向量
    基于QPSK的载波同步和定时同步性能仿真,包括Costas环的gardner环
    掌握 Web3 的关键工具:9大宝藏APP助你玩转区块链
    pb:10个用一条语句写成的有关日期函数
    [CISCN2019 华北赛区 Day1 Web1]Dropbox
  • 原文地址:https://blog.csdn.net/xuezhangjun0121/article/details/132711399