在golang中使用协程非常方便,如果有大量任务要处理,且任务间没有关联,可以并行同时处理的话,就非常适合用golang的协程处理。串行一个个执行需要的效率,远没有并行同时处理来的快,特别是当处理每个任务需要的时间越长,使用并行效果就越明显。
是的,golang就是利用多核cpu的云时代语言。
有些时候,golang起的协程特别多的话,机器的性能或其他服务组件会扛不住,比如服务器IO,数据库连接等,这时候需要主动控制协程并发数,避免服务崩溃。
下面是golang控制协程并发数的几种方法,很有意思。
代码实践:
方法一:使用有缓冲容量长度的channel控制
- package main
-
- import (
- "fmt"
- "time"
- )
-
- //同时最多10个协程运行
- var limitMaxNum = 10
- var chData = make(chan int, limitMaxNum)
-
- //有100个任务要处理
- var tasknum = 100
-
- //使用 有缓冲容量长度的channel
- func main() {
- var i, j int
- var chanRet = make(chan int, tasknum) //运行结果存储到chanRet
-
- //运行处理
- go func() {
- for i = 0; i < tasknum; i++ {
- chData <- 1
- go dotask(i, chanRet)
- }
- }()
-
- //获取返回结果
- for j = 0; j < tasknum; j++ {
- <-chData
- <-chanRet
- // fmt.Println("ret:", ret)
- }
- fmt.Println("main over")
- }
-
- func dotask(taskid int, chanRet chan int) {
- time.Sleep(time.Millisecond * 100)
- fmt.Println("finish task ", taskid)
-
- chanRet <- taskid * taskid
- }
应用场景:适合知道任务数量,最简单的使用方式。
方法2:使用channel+waitGroup
- package main
-
- import (
- "fmt"
- "sync"
- "time"
- )
-
- var limitMaxNum = 10
- var chData = make(chan int, limitMaxNum)
- var jobGroup sync.WaitGroup
- var tasknum = 100
-
- //使用 有缓冲容量长度的channel
- func main() {
- var i int
- //var chanRet = make(chan int, tasknum)
-
- //处理任务,最多同时有10个协程
- for i = 0; i < tasknum; i++ {
- chData <- 1
- go dotask(i)
- }
-
- //使用Wait等待所有任务执行完毕
- jobGroup.Wait()
- fmt.Println("main over")
- }
-
- func dotask(taskid int) {
- jobGroup.Add(1)
-
- time.Sleep(time.Millisecond * 100)
- fmt.Println("finish task ", taskid)
-
- // fmt.taskid * taskid
- <-chData
-
- jobGroup.Done()
- }
应用场景:waitGroup开箱即用,不管任务数量是否提前清楚的情况下,都可以用,也很简单,少了次循环。
方法3:优雅使用waitGroup+channel+range
- package main
-
- import (
- "fmt"
- "sync"
- "time"
- )
-
- var limitMaxNum = 10
- var chData = make(chan int, limitMaxNum)
- var jobGroup sync.WaitGroup
- var tasknum = 100
-
- func main() {
- var i int
- var j int
-
- //组装任务
- chanTask := make(chan int, tasknum)
- for j = 0; j < tasknum; j++ {
- chanTask <- j
- }
- close(chanTask)
-
- jobGroup.Add(tasknum)
- for i = 0; i < limitMaxNum; i++ { //最多10个协程
- go dotask3(chanTask)
- }
-
- jobGroup.Wait()
- fmt.Println("main over")
- }
-
- func dotask3(taskChan chan int) {
- for taskid := range taskChan { //每个协程拼命抢夺任务,直到任务完结
- time.Sleep(time.Millisecond * 500)
- fmt.Println("finish task ", taskid)
- jobGroup.Done()
- }
- }
应用场景:这个处理方式最优雅,不仅控制了同时运行的只有10个协程,而且整个运行过程中只起了10个协程(当然不包括主协程),处理方式也很优雅,高效利用了channel 是否阻塞特点,最推荐这个。