Worker pool(goroutine池)
在工作中我们通常会使用可以指定启动的goroutine数量–worker pool
模式,控制goroutine
的数量,防止goroutine
泄漏和暴涨。
funcmain(){jobs:=make(chanint,100)results:=make(chanint,100)//开启3个goroutineforid:=0;id<3;id++{goworker(id,jobs,results)}//生成5个jobfornum:=0;num<5;num++{jobs<-num}close(jobs)//输出结果fora:=0;a<5;a++{<-results}//死锁,只有close(results)可用//for{//x,ok:=<-results//if!ok{//break//}//fmt.Println(x)//}//死锁,只有close(results)可用//forx:=rangeresults{//fmt.Println(x)//}}funcworker(idint,jobs<-chanint,resultschan<-int){forj:=rangejobs{time.Sleep(time.Second)fmt.Printf("JobID:{%d}Jobis:%d\n",id,j)results<-j*2}}
输出结果如下:
JobID:{0}Jobis:0JobID:{2}Jobis:2JobID:{1}Jobis:1JobID:{2}Jobis:4JobID:{0}Jobis:3
Select多路复用
在某些场景下我们需要同时从多个通道接收数据。通道在接收数据时,如果没有数据可以接收将会发生阻塞。你也许会写出如下代码使用遍历的方式来实现:
for{//尝试从ch1接收值data,ok:=<-ch1//尝试从ch2接收值data,ok:=<-ch2…}
这种方式虽然可以实现从多个通道接收值的需求,但是运行性能会差很多。为了应对这种场景,Go内置了select
关键字,可以同时响应多个通道的操作。
select
的使用类似于switch语句,它有一系列case分支和一个默认的分支。每个case会对应一个通道的通信(接收或发送)过程。select
会一直等待,直到某个case
的通信操作完成时,就会执行case
分支对应的语句。具体格式如下:
select{case<-ch1:...casedata:=<-ch2:...casech3<-data:...default:默认操作}
举个小例子来演示下select
的使用:
funcmain(){ch:=make(chanint,1)fori:=0;i<10;i++{select{casex:=<-ch:fmt.Println(x)casech<-i:fmt.Printf("%d放进去了\n",i)}}}
使用select
语句能提高代码的可读性。
可处理一个或多个channel的发送/接收操作。
如果多个case
同时满足,select
会随机选择一个。
对于没有case
的select{}
会一直等待,可用于阻塞main函数。