深入理解Golang中的函数式编程与Channel | 青训营笔记

69 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记

函数式编程

函数式编程 vs 函数指针

  • 函数是一等公民:参数,变量,返回值都可以是函数
  • 高阶函数
  • 函数 -> 闭包

Go语言是通用语言

“正统”函数式编程(了解)

  • 不可变性:不能有状态,只有常量和函数
  • 函数只能有一个参数

函数与闭包

函数体内包含局部变量自由变量

Go语言闭包的应用

为函数实现接口**

 package main
 ​
 import (
     "bufio"
     "fmt"
     "io"
     "strings"
 )
 ​
 func fibonacci() intGen {
     a, b := 0, 1
     return func() int {
         a, b = b, a+b
         return a
     }
 }
 ​
 type intGen func() int
 ​
 func (g intGen) Read(p []byte) (n int, err error) {
     next := g()
     if next > 100000 {
         return 0, io.EOF
     }
     s := fmt.Sprintf("%d\n", next)
     
     //TODO: incorrect if p is too small!
     return strings.NewReader(s).Read(p)
 }
 ​
 func printFileContents(reader io.Reader) {
     scanner := bufio.NewScanner(reader)
 ​
     for scanner.Scan() {
         fmt.Println(scanner.Text())
     }
 }
 ​
 func main() {
     f := fibonacci()
     printFileContents(f)
 ​
 }
  • 更为自然,不需要修饰如何访问自由变量
  • 没有Lambda表达式,但是有匿名函数

错误处理

defer调用

  • 确保在函数结束时调用
  • 参数在defer语句时计算
  • defer列表为后进先出Stack

出错处理

 func writeFile(filename string) {
     file, err := os.OpenFile(
         filename, os.O_EXCL|os.O_CREATE, 0666)
     if err != nil {
         if PathError, ok := err.(*os.PathError); !ok {
             panic(err)
         } else {
             fmt.Println("%s, %s, %s\n", PathError.Op, PathError.Path, PathError.Err)
         }
         return
     }
 }

自己生成错误

err = errors.New("This is a custom error")

测试

Debugging sucks; Testing Rocks!

Goroutine

协程Coroutine

  • 轻量级“线程”
  • 非抢占式多任务处理,由协程主动交出控制权
  • 编译器/解释器/虚拟机层面的多任务
  • 多个协程可能在一个或多个线程上运行
  • 子程序是协程的一个特例 -Donnald Knuth

goroutine.jpg

协程.jpg

  • 任何函数只需加上go就能送给调度器运行
  • 不需要在定义时区分是否为异步函数
  • 调度器在合适的点进行切换,尽管是非抢占式
  • 使用-race来检测数据访问冲突

eg:Python中的协程使用yield关键字实现协程,Python3.5 加入了async def对协程原生支持

Goroutine可能的切换点

  • I/O ,select
  • 函数调用(有时)
  • channel
  • runtime.Gosched()
  • 等待锁
  • 只是参考,不能保证切换,不能保证在其他地方不切换

Channel

  • channel
  • buffered channel
  • range
  • 理论基础:Communication Sequential Process(CSP)

不用通过共享内存来通信;通过通信来共享内存

Channel close : 发送方去close

接收方判断方法 1. ok 2.range语法

实例1:系统自带WaitGroup等待多人

 package main
 ​
 import (
     "fmt"
     "sync"
 )
 ​
 func doWork(id int, c chan int, w worker) {
     for n := range w.in {
 ​
         fmt.Printf("Worker %d received %c\n", id, n)
         w.done()
     }
 }
 ​
 type worker struct {
     in   chan int
     done func()
 }
 ​
 func createWorker(id int, wg *sync.WaitGroup) worker {
     w := worker{
         in: make(chan int),
         done: func() {
             wg.Done()
         },
     }
     go doWork(id, w.in, w)
     return w
 }
 ​
 func chanDemo() {
     var workers [10]worker
     var wg sync.WaitGroup
     for i := 0; i < 10; i++ {
         workers[i] = createWorker(i, &wg)
     }
 ​
     wg.Add(20)
 ​
     for i, worker := range workers {
         worker.in <- 'a' + i
     }
     for i, worker := range workers {
         worker.in <- 'A' + i
 ​
     }
 ​
     wg.Wait()
 ​
 }
 ​
 func main() {
 ​
     chanDemo()
 ​
 }