Go语言并发之道--笔记3

pipeline
可以用来在系统中形成抽象的一种工具,尤其是程序需要流式处理或者批处理数据
一系列的数据输入,执行操作并将结果数据传回的系统,称这些操作是pipeline的一个stage

构建一个pipeline的tage
pipeline stage函数
multiply := func(values []int,multiplier int) []int {
   multipliedValues := make([]int,len(values))
   for i,v := range values {
      multipliedValues[i] = v * multiplier
   }
   return multipliedValues
}

add := func(values []int,additive int) []int {
   addedValues := make([]int,len(values))
   for i,v := range values {
      addedValues[i] = v + additive
   }
   return addedValues
}

ints := []int{1,2,3,4}
for _,v := range add(multiply(ints,2),1) {
   fmt.Println(v)
}

ints := []int{1,2,3,4}
for _,v := range multiply(add(multiply(ints,2),1),2) {
   fmt.Println(v)
}


//将stage转换为以流为导向
multiply := func(value,multiplier int) int {
   return value * multiplier
}
add := func(value,additive int) int {
   return value + additive
}
ints := []int{1,2,3,4}
for _,v := range ints {
   fmt.Println(multiply(add(multiply(v,2),1),2))
}




构建pipeline的最佳实践
   generator := func(done <-chan interface{},integers ...int) <-chan int {
      intStream := make(chan int)
      go func() {
         defer close(intStream)
         for _,i := range integers {
            select {
            case <-done:
               return
            case intStream <- i:
            }
         }
      }()
      return intStream
   }
   multiply := func(
      done <-chan interface{},
      intStream <-chan int,
      multiplier int,
   ) <-chan int {
      multipliedStream := make(chan int)
      go func() {
         defer close(multipliedStream)
         for i := range intStream {
            select {
            case <-done:
               return
            case multipliedStream <- i*multiplier:
            }
         }
      }()
      return multipliedStream
   }

   add := func(
      done <-chan interface{},
      intStream <-chan int,
      additive int,
   ) <-chan int {
      addedStream := make(chan int)
      go func() {
         defer close(addedStream)
         for i := range intStream {
            select {
            case <-done:
               return
            case addedStream <- i+additive:
            }
         }
      }()
      return addedStream
   }

   done := make(chan interface{})
   defer close(done)

   intStream := generator(done,1,2,3,4)
   pipeline := multiply(done,add(done,multiply(done,intStream,2),1),2)

   for v := range pipeline {
      fmt.Println(v)
   }



一些便利的生成器
pipeline的生成器是将一组离散值转换为channel撒谎那个的值流的任何函数

//repeat的生成器
repeat := func(
   done <-chan interface{},
   values ...interface{},
)<-chan interface{} {
   valueStream := make(chan interface{})
   go func() {
      defer close(valueStream)
      for {
         for _,v := range values {
            select {
               case <-done:
                  return
               case valueStream <- v:
            }
         }
      }
   }()
   return valueStream
}

//通用pipeline stage,重复使用时很有用
take := func(
   done <-chan interface{},
   valueStream <-chan interface{},
   num int,
) <-chan interface{} {
   takeStream := make(chan interface{})
   go func() {
      defer close(takeStream)
      for i:=0;i<num;i++ {
         select {
            case <-done:
               return
            case takeStream <- valueStream:
         }
      }
   }()
   return takeStream
}

done := make(chan interface{})
defer close(done)

for num := range take(done,repeat(done,1),10) {
   fmt.Printf("%v",num)
}



//创建一个重复调用函数的生成器repeatFn
   //通用pipeline stage,重复使用时很有用
   take := func(
      done <-chan interface{},
      valueStream <-chan interface{},
      num int,
   ) <-chan interface{} {
      takeStream := make(chan interface{})
      go func() {
         defer close(takeStream)
         for i:=0;i<num;i++ {
            select {
            case <-done:
               return
            case takeStream <- valueStream:
            }
         }
      }()
      return takeStream
   }


   //创建一个重复调用函数的生成器repeatFn
   repeatFn := func(
      done <-chan interface{},
      fn func() interface{},
   ) <-chan interface{} {
      valueStream := make(chan interface{})
      go func() {
         defer close(valueStream)
         for {
            select {
            case <-done:
               return
            case valueStream <- fn():
            }
         }
      }()
      return valueStream
   }

   //生成10个随机数字
   done := make(chan interface{})
   defer close(done)

   rand := func() interface{} { return rand.Int()}    //导入库"math/rand"
   for num := range take(done,repeatFn(done,rand),10) {
      fmt.Println(num)
   }


//toString pipeline stage
//报错
toString := func(
   done <-chan interface{},
) <-chan string {
   stringStream := make(chan string)
   go func() {
      defer close(stringStream)
      for v := range valueStream {
         select {
            case <-done:
               return
            case stringStream <- v.(string):
         }
      }
   }()
   return stringStream
}

done := make(chan interface{})
defer close(done)

var message string
for token := range toString(done,take(done,repeat(done,"I","am."),5)) {
   message += token
}
fmt.Pringf("message: %s...",message)


//基准测试函数,一个测试通用stage,另一个测试特定stage
func BenchmarkGeneric(b *testing.B) {
   done := make(chan interface{})
   defer close(done)

   b.ResetTimer()
   for range toString(done,take(done,repeat(done,"a"),b.N)) {
   }
}

func BenchmarkTyped (b *testing.B) {
   repeat := func(done <-chan interface{},values ...string) <-chan string {
      valueStream := make(chan string)
      go func() {
         defer close(valueStream)
         for {
            for _,v := range values {
               select {
               case <-done:
                  return
               case valueStream <- v:
               }
            }
         }
      }()
      return valueStream
   }

   take := func(
      done <-chan interface{},
      valueStream <-chan string,
      num int,
   ) <-chan string {
      takeStream := make(chan string)
      go func() {
         defer close(takeStream)
         for i := num; i>0 || i == -1; {
            if i != -1 {
               I --
            }
            select {
            case <-done:
               return
            case takeStream <- <-valueStream:
            }
         }
      }()
      return takeStream
   }
   done := make(chan interface{})
   defer close(done)

   b.ResetTimer()
   for range take(done,repeat(done,"a"),b.N){
   }
}




扇入,扇出
扇出用于描述启动多个goroutine以处理来自pipeline的输入的过程,
扇入是描述将多个结果组合到一个channel的过程中的术语。

示例:找到素数菲常低效的函数
rand := func() interface{} { return rand.Intn(50000000) }
done := make(chan interface{})
defer close(done)
start := time.Now()
randIntStream := toInt(done,repeatFn(done,rand))
fmt.Println("Primes:")
for prime := range take(done,primeFinder(done,randIntStream),10) {
   fmt.Printf("\t%d\n",prime)
}
fmt.Printf("Search took: %v",time.Since(start))


优化后的函数
fanIn := func(
   done <-chan interface{},
   channels ...<-chan interface{},
) <-chan interface{} {    //
   var wg sync.WaitGroup  //
   multiplexedStream := make(chan interface{})

   multiplex := func(c <-chan interface{}) {  //
      defer wg.Done()
      for i := range c {
         select {
         case <-done:
            return
         case multiplexedStream <- i:
         }
      }
   }

   //从所有的channel里取值
   wg.Add(len(channels))  //
   for _,c := range channels {
      go multiplex(c)
   }

   //等待所有的读操作结束
   go func() {    //
      wg.Wait()
      close(multiplexedStream)
   }()
   return multiplexedStream
}

done := make(chan interface{})
defer close(done)

start := time.Now()

rand := func() interface{} { return rand.Intn(50000000)}

randIntStream := toInt(done,repeatFn(done,rand))

numFinders := runtime.NumCPU()
fmt.Printf("Spinning up %d prime finders.\n",numFinders)
finders := make([]<-chan interface{},numFinders)
fmt.Println("Primes:")
for i := 0; i < numFinders; i++ {
   finders[i] = primeFinder(done,randIntStream)
}
for prime := range take(done,fanIn(done,finders...),10) {
   fmt.Printf("\t%d\n",prime)
}

fmt.Printf("Search took: %v",time.Since(start))




or-done-channel
示例:
orDone := func(done,c <-chan interface{}) <-chan interface{} {
   valStream := make(chan interface{})
   go func() {
      for {
         select {
            case <-done:
               return
            case v,ok := <-c:
               if ok == false {
                  return
               }
               select {
               case valStream <- v:
               case <-done:
               }
         }
      }
   }()
   return valStream
}

for val := range orDone(done,muchan) {
   //用val执行某些操作
}



tee-channel
将tee-channel传递给一个读channel,其会返回两个单独的channel,
tee := func(
   done <-chan interface{},
   in <-chan interface{},
) (_,_<-chan interface{}) {<-chan interface{}} {
   out1 := make(chan interface{})
   out2 := make(chan interface{})
   go func() {
      defer close(out1)
      defer close(out2)
      for val := range orDone(done,in) {
         var out1,out2 = out1,out2
         for i := 0;i<2;i++ {
            select {
               case <-done:
               case out1<-val:
                  out1 = nil //写入channel后,将副本设置为nil,以便进一步阻塞写入
               case out2<-val
                  out2 = nil
            }
         }
      }
   }()
   return out1,out2
}

done := make(chan interface{})
defer close(done)

out1,out2 := tee(done,take(done,repeat(done,1,2)),4)

for val1 := range out1 {
   fmt.Printf("out1: %v,out2: %v\n",val1,<-out2)
}



桥接channel模式
<-chan <-chan interface{}

//处理一个充满channel的channel,将其拆解为一个简单的channel
bridge := func(
   done <-chan interface{},
   chanStream <-chan <-chan interface{},
) <-chan interface{} {
   valStream := make(chan interface{})    //将返回bridge中的所有值的channel
   go func() {
      defer close(valStream)
      for {  //从chanStream中提取channel并将其提供给嵌套循环来使用
         var stream <-chan interface{}
         select {
            case maybeStream,ok := <-chanStream:
               if ok==false {
                  return
               }
               stream = maybeStream
            case <-done:
               return
         }
         for val := range orDone(done,stream) { //读取已给出的channel值,并将其重复到valStream中
            select {
               case valStream <- val:
               case <-done:
            }
         }
      }
   }()
   return valStream
}

//创建10个channel,每个channel写入一个元素,并将这些channel传递给桥接函数:
genVals := func() <-chan <-chan interface{} {
   chanStream := make(chan (<-chan interface{}))
   go func() {
      defer close(chanStream)
      for i := 0;i<10;i++ {
         stream := make(chan interface{},1)
         stream <- i
         close(stream)
         chanStream <- stream
      }
   }()
   return chanStream
}
for v := range bridge(nil,genVals()) {
   fmt.Printf("%v ",v)
}



队列排队
队列:在队列尚未准备好的时候开始接受请求

//缓冲写入队列和未缓冲写入队列的简单比较
func BenchmarkUnbufferedWrite(b *testing.B) {
   perfornWrite(b,tmpFileOrFatal())
}

func BenchmarkBufferedWrite(b *testing.B) {
   bufferedFile := bufio.NewWriter(tmpFileOrFatal())
   performWrite(b,bufio.NewWriter(bufferredFile))
}

func timeFileOrFatal() *os.File {
   file,err := ioutil.TempFile("","tmp")
   iferr != nil {
      log.Fatal("error: %v",err)
   }
   return file
}

func performWrite(b *testing.B,write io.Writer) {
   done := make(chan interface{})
   defer close(done)

   b.ResetTimer()
   for bt := range take(done,repeat(done,byte(0)),b.N) {
      writer.Writer([]byte{bt.(byte)})
   }
}

go test -bench=. buffering_test.go

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本书作者带你一步一步深入这些方法。你将理解 Go语言为何选定这些并发模型,这些模型又会带来什么问题,以及你如何组合利用这些模型中的原语去解决问题。学习那些让你在独立且自信的编写与实现任何规模并发系统时所需要用到的技巧和工具。 理解Go语言如何解决并发难以编写正确这一根本问题。 学习并发与并行的关键性区别。 深入到Go语言的内存同步原语。 利用这些模式中的原语编写可维护的并发代码。 将模式组合成为一系列的实践,使你能够编写大规模的分布式系统。 学习 goroutine 背后的复杂性,以及Go语言的运行时如何将所有东西连接在一起。 作者简介 · · · · · · Katherine Cox-Buday是一名计算机科学家,目前工作于 Simple online banking。她的业余爱好包括软件工程、创作、Go 语言(igo、baduk、weiquei) 以及音乐,这些都是她长期的追求,并且有着不同层面的贡献。 目录 · · · · · · 前言 1 第1章 并发概述 9 摩尔定律,Web Scale和我们所陷入的混乱 10 为什么并发很难? 12 竞争条件 13 原子性 15 内存访问同步 17 死锁、活锁和饥饿 20 确定并发安全 28 面对复杂性的简单性 31 第2章 对你的代码建模:通信顺序进程 33 并发与并行的区别 33 什么是CSP 37 如何帮助你 40 Go语言并发哲学 43 第3章 Go语言并发组件 47 goroutine 47 sync包 58 WaitGroup 58 互斥锁和读写锁 60 cond 64 once 69 池 71 channel 76 select 语句 92 GOMAXPROCS控制 97 小结 98 第4章 Go语言并发模式 99 约束 99 for-select循环103 防止goroutine泄漏 104 or-channel 109 错误处理112 pipeline 116 构建pipeline的最佳实践 120 一些便利的生成器 126 扇入,扇出 132 or-done-channel 137 tee-channel 139 桥接channel模式 140 队列排队143 context包 151 小结 168 第5章 大规模并发 169 异常传递169 超时和取消 178 心跳 184 复制请求197 速率限制199 治愈异常的goroutine 215 小结 222 第6章 goroutine和Go语言运行时 223 工作窃取223 窃取任务还是续体 231 向开发人员展示所有这些信息 240 尾声 240 附录A 241

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值