在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 是否阻塞特点,最推荐这个。