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类型)

  1. chan <- int (send only)
  2. <- chan int (receive only)

<- 在chan后面的是send only ,<- 在chan前面的的receive only。
这种套路还是挺好用的,流水线了特别适合处理数据,比如说先获取,然后加工,最后再存到数据库中,如果传统那种做法是先获取数据,然后存到内存,之后处理数据,最后再存数据,时间上会比较长,也就是说前面的没做完后面的进行不了,空间上也很浪费,要把中间结果集存到内存中,数据量大的话内存还容易爆。

原文地址:https://www.cnblogs.com/gqdw/p/13031693.html