go channel pipeline 套路
go channel pipeline 套路
1. 起源
一直想写关于pipeline的,但是深知自己在编程方面还是个菜,所以就直接引用the go programming language里面的例子了。照着写不容易出错。此书是永远的经典。
2. pipeline1
package main
import "fmt"
func main() {
naturals := make(chan int)
squares := make(chan int)
go func() {
for x := 0; ; x++ {
naturals <- x
}
}()
go func() {
for {
x := <-naturals
squares <- x * x
}
}()
for {
fmt.Println(<-squares)
}
}
所谓pipeline,中文就是流水线,函数通过chan传递变量,不需要传参数那种方式硬耦合了。
这个例子中没有close,所以程序无限循环下去。
当channel close的时候,再取就会失败,
x,ok := <- naturals
if !ok{
break
}
为此go语言简化了此类操作,设计了range的语法,很方便。
3. pipeline2
package main
import "fmt"
func main() {
naturals := make(chan int)
squares := make(chan int)
go func() {
for x := 0; x < 100; x++ {
naturals <- x
}
close(naturals)
}()
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}()
for x := range squares {
fmt.Println(x)
}
}
close了以后,整个程序优雅结束。
但是用匿名函数总不是长久之计,可以写成有名func。
4. pipeline3
package main
import "fmt"
func couter(out chan<- int) {
for x := 0; x < 100; x++ {
out <- x
}
close(out)
}
func squarer(out chan<- int, in <-chan int) {
for v := range in {
out <- v * v
}
close(out)
}
func printer(in <-chan int) {
for v := range in {
fmt.Println(v)
}
}
func main() {
natuals := make(chan int)
squares := make(chan int)
go couter(natuals)
go squarer(squares, natuals)
printer(squares)
}
go语言支持了定义unidirectional channel types(单向channel类型)
- chan <- int (send only)
- <- chan int (receive only)
<- 在chan后面的是send only ,<- 在chan前面的的receive only。
这种套路还是挺好用的,流水线了特别适合处理数据,比如说先获取,然后加工,最后再存到数据库中,如果传统那种做法是先获取数据,然后存到内存,之后处理数据,最后再存数据,时间上会比较长,也就是说前面的没做完后面的进行不了,空间上也很浪费,要把中间结果集存到内存中,数据量大的话内存还容易爆。