go-channel实践1

niyuelin1990 · · 1157 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

        某些信息从旧的资源池中取出来,经过一些加工处理,再放入新的资源池中,这个过程如果按传统的方式就是采用完全串行的方式效率会很低,粒度太粗了,具体的粒度可以细化以每次所取的单位资源为粒度。

有一个资源池存储这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,负责传递的信息分别是什么。


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:niyuelin1990

查看原文:go-channel实践1

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1157 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传