Golang无限开启Goroutine?该如何限定Goroutine数量?

一、Goroutine 具备以下两个特点:

  • 体积轻量(占内存小,一个 2kb 左右)

  • 优秀的 GMP 调度

    我们如果迅速的开启 goroutine (不控制并发的 goroutine 数量 )的话,会在短时间内占据操作系统的资源(CPU、内存、文件描述符等)。

  • CPU 使用率浮动上涨

  • Memory 占用不断上涨

  • 主进程崩溃(被杀掉了)

二、控制 goroutine 的几种方法
方法一:用有 buffer 的 channel 来限制

package main
​
import (
  "fmt"
  "math"
  "runtime"
)// 模拟执行业务的 goroutine
// runtime.NumGoroutine()表示获取协程的数量
func doBusiness(ch chan bool, i int) {
  fmt.Println("i的值:", i, "协程数:", runtime.NumGoroutine())
  <-ch
}func main() {
  max := math.MaxInt64
  fmt.Println(max)
​
  ch := make(chan bool, 3)for i := 0; i < max; i++ {
    ch <- true
    go doBusiness(ch, i)
  }
}

打印的结果:

...
i的值: 101058 协程数: 4
i的值: 101059 协程数: 4
i的值: 101060 协程数: 4
i的值: 101061 协程数: 4
i的值: 101062 协程数: 4
i的值: 101063 协程数: 4
i的值: 101064 协程数: 4
i的值: 101065 协程数: 4
i的值: 101066 协程数: 4
i的值: 101067 协程数: 4
i的值: 101068 协程数: 4
i的值: 101069 协程数: 4
i的值: 101070 协程数: 4
i的值: 101071 协程数: 4
i的值: 101072 协程数: 4
i的值: 101073 协程数: 4
i的值: 101074 协程数: 4
i的值: 101075 协程数: 4
...

从结果看,程序并没有出现崩溃,而是按部就班的顺序执行,并且 go 的数量控制在了 3,(4 的原因是因为还有一个 main goroutine)
但是这段代码有一个小问题,就是如果我们把 go_cnt 的数量变的小一些,会出现打出的结果不正确。

package main
​
import (
  "fmt"
  "runtime"
)// 模拟执行业务的 goroutine
// runtime.NumGoroutine()表示获取协程的数量
func doBusiness(ch chan bool, i int) {
  fmt.Println("i的值:", i, "协程数:", runtime.NumGoroutine())
  <-ch
}func main() {
  //max := math.MaxInt64
  max := 10
  fmt.Println(max)
​
  ch := make(chan bool, 3)for i := 0; i < max; i++ {
    ch <- true
    go doBusiness(ch, i)
  }
}


结果:

10
i的值: 0 协程数: 2
i的值: 1 协程数: 2
i的值: 2 协程数: 2
i的值: 3 协程数: 2
i的值: 4 协程数: 2
i的值: 5 协程数: 2
i的值: 6 协程数: 2
i的值: 7 协程数: 2
i的值: 8 协程数: 2


可以从上面的实例中看出来有些 goroutine 没有打印出来,是由于 main 把所有 goroutine 开启之后,main 就直接退出了,我们知道 main 进程退出,低下所有的 goroutine 都会结束掉,从而导致有些 goroutine 还没来得及执行就退出了。所以想全部 go 都执行,需要在 main 的最后进行阻塞操作。
方法二:使用 sync 同步机制

package main
​
import (
  "fmt"
  "math"
  "runtime"
  "sync"
)var wg = sync.WaitGroup{}func doBusiness(i int) {
​
  fmt.Println("i的值 ", i, " 协程的数量为 = ", runtime.NumGoroutine())
  wg.Done()
}func main() {
  //模拟用户需求业务的数量
  max := math.MaxInt64
​
  for i := 0; i < max; i++ {
    wg.Add(1)
    go doBusiness(i)
  }
​
  wg.Wait()
}


很明显,如果单纯的使用 sync 也达不到控制 goroutine 的数量,最终结果依然是崩溃。​

方法三:channel 与 sync 同步组合方式实现控制 goroutine

package main
​
import (
  "fmt"
  "math"
  "runtime"
  "sync"
)var wg = sync.WaitGroup{}func doBusiness(ch chan bool, i int) {
​
  fmt.Println("i的值为 ", i, " 协程数量为 = ", runtime.NumGoroutine())<-ch
​
  wg.Done()
}func main() {
  //模拟用户需求go业务的数量
  max := math.MaxInt64
​
  ch := make(chan bool, 3)for i := 0; i < max; i++ {
    wg.Add(1)
    ch <- true
    go doBusiness(ch, i)
  }
​
  wg.Wait()
}
​


方法四:利用无缓冲 channel 与任务发送/执行分离方式
package main
​
import (
  "fmt"
  "math"
  "runtime"
  "sync"
)var wg = sync.WaitGroup{}func doBusiness(ch chan int) {for t := range ch {
    fmt.Println("go task = ", t, ", goroutine count = ", runtime.NumGoroutine())
    wg.Done()
  }
}func sendTask(task int, ch chan int) {
  wg.Add(1)
  ch <- task
}func main() {
​
  ch := make(chan int) //无buffer channel
​
  goCnt := 3 //启动goroutine的数量
  for i := 0; i < goCnt; i++ {
    //启动go
    go doBusiness(ch)
  }
​
  taskCnt := math.MaxInt64 //模拟用户需求业务的数量
  for t := 0; t < taskCnt; t++ {
    //发送任务
    sendTask(t, ch)
  }
​
  wg.Wait()
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

原克技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值