某些信息从旧的资源池中取出来,经过一些加工处理,再放入新的资源池中,这个过程如果按传统的方式就是采用完全串行的方式效率会很低,粒度太粗了,具体的粒度可以细化以每次所取的单位资源为粒度。
有一个资源池存储这person的信息,将每个person从中取出来,之后进行一些处理,再存到新的资源池中,这里用oldarray以及newarray来模拟旧的和新的资源池:
代码如下:
type Person struct { name string age int addr string } var oldpersonarray = [5]Person{} var newpersonarray = [5]Person{} type PersonHandler interface { Batch(origs <-chan Person) <-chan Person Handle(orig *Person) } //struct 实现了personhandler 接口 type PersonHandlerImpl struct{} //从origs接收信息 处理之后再返回给新的channel func (handler PersonHandlerImpl) Batch(origs <-chan Person) <-chan Person { dests := make(chan Person, 100) go func() { for { p, ok := <-origs if !ok { close(dests) break } handler.Handle(&p) log.Printf("old value : %v\n", p) //time.Sleep(time.Second) dests <- p } }() return dests } //这里要使用引用传递 func (handler PersonHandlerImpl) Handle(orig *Person) { orig.addr = "new address" } func getPersonHandler() PersonHandler { return &PersonHandlerImpl{} } //print the oldpersonarray into the chan<-Person func fetchPerson(origs chan<- Person) { for _, v := range oldpersonarray { fmt.Printf("get the value : %v \n", v) time.Sleep(time.Second) origs <- v } close(origs) } //fetch the value from the channel and store it into the newpersonarray func savePerson(dest <-chan Person) <-chan int { intChann := make(chan int) go func() { index := 0 for { p, ok := <-dest if !ok { break } time.Sleep(time.Second) log.Printf("new value transfer %v \n", p) newpersonarray[index] = p index++ } intChann <- 1 }() return intChann } func init() { //使用range的话是值传递 这里要给oldpersonarray赋值进来 tmplen := len(oldpersonarray) for i := 0; i < tmplen; i++ { oldpersonarray[i].addr = "old address" oldpersonarray[i].age = i oldpersonarray[i].name = strconv.Itoa(i) } log.Printf("first print init value : %v\n", oldpersonarray) } func main() { handeler := getPersonHandler() origs := make(chan Person, 100) dests := handeler.Batch(origs) go func() { fetchPerson(origs) }() // 不加go func的话 要等这句执行完 才能执行下一句 // 则orgis信息都输出 完全关闭掉 这个时候 从dest接收信息的语句才开始执行 // 所以不会动态输出 这句加上go func的话 就会没隔 1s 动态输出 // 如果将fetchPerson 再往前面放一句 则old value也不会动态输出 //fetchPerson(origs) sign := savePerson(dests) log.Println(<-sign) log.Printf("last print new value : %v \n", newpersonarray) }
- 首先声明一个 PersonHandler 的接口,之后声明一个struct PersonHandlerImpl 将接口中的两个方法都实现了,init函数用于进行oldarray的初始化工作。注意为了减少出错,内部的函数在方声明的时候都是单向的channel。
- 1,2 fetchperson从oldarray中区数据,并把数据存到origs channel中,注意最后取完数据到通道之后,要由发送方将channel关闭,否则可能造成deadlock。注意在main函数中,如果fech操作没有放到一个goroutine中来执行,就仍然是串行的,相当于是把数据都放入到channel中,另一端才开始取,没发挥出并发的优势。
- 3,4 Batch函数将person信息从origs中取出来,进行处理后,同时传到dests中,最后将dests返回,注意这里不是全部传入之后才将dests返回,而是新启动一个goroutine执行传入操作,同时将dests返回,注意要主动关闭channel。
- 5 savePerson操作接收一个<-chann 之后从中接受person信息,将值写入到新的资源池中,最后全部写入结束之后,传一个sign channel给主进程,结束。
- 总结,在需要动态输出信息的时候,goroutine往往是和channel结合在一起使用。最常见的用法是,一个goroutine负责向channel中写入数据,之后将channel返回,由其他进程取出信息。比如之前写过的一些websocket从前台接受信息,后台处理信息之后再动态返回给前台打出结果的模型,就和这个差不多,总之具体的异步执行流程要理清楚,都有哪些channel,负责传递的信息分别是什么。
有疑问加站长微信联系(非本文作者)