Go 进阶 · 分布式爬虫实战 学习笔记 day01

850 阅读2分钟

你好,我是一名phper转go的小白,从这篇文章开始,我会对郑建勋老师的极客专栏《Go 进阶 · 分布式爬虫实战》进行一个持续的研读和笔记总结。

今天要分享是的go的基础知识部分,go的基础知识部分可以分为6个部分,分别是开发环境、基础语法、语法特性、并发编程、项目组织、工具与库。 day01先讲一下:语法特性、并发编程。

语法特性

主要特性包括:defer、interface、channel、goroutine

defer

特点:延迟执行、参数预计算、LIFO执行顺序 场景:捕获异常(recover),释放资源

接口

接口分为“带方法的接口”和“空接口”,带方法的接口内部有一些列方法的签名,只要全部实现了这些方法,我们就可以说,该类型实现了该接口;对于空接口,它可以存储任意类型,比较灵活,当需要知道该类型的具体类型时,需要使用到断言。

并发编程

  • 进程、线程、协程,进程是操作系统资源分配的基本单位,线程是操作系统资源调度的基本单位。而协程位于用户态,是在线程基础上构建的轻量级调度单位。
  • 并发与并行,并行指的是同一时刻做很多时间,并发指的是同一段时间内做很多事情。
  • 主协程和子协程,main 函数是特殊的主协程,它退出之后整个程序都会退出。而其他的协程都是子协程,子协程退出之后,程序正常运行。 Go 语言实现了 CSP 并发编程模式,把通道当作 Go 语言中的一等公民,通道的基本使用方式包括下面几点。
  • 通道的声明和初始化:
chan int
chan <- float
<-chan string
  • 通道写入和读取数据,关闭
c <- 5

data := <-c

close(c)
  • select 监听多个通道实现多路复用。当 case 中多个通道状态准备就绪时,select 随机选择一个分支进行执行:

select {
  case <-ch1:
    // ...
  case x := <-ch2:
    // ...use x...
  case ch3 <- y:
    // ...
  default:
    // ...
  }
  • 用 context 来处理协程的优雅退出和级联退出

func Stream(ctx context.Context, out chan<- Value) error {
  for {
    v, err := DoSomething(ctx)
    if err != nil {
      return err
    }
    select {
    case <-ctx.Done():
      return ctx.Err()
    case out <- v:
    }
  }
  • 传统的同步原语:原子锁。Go 提供了 atomic 包用于处理原子操作

func add() {
  for {
    if atomic.CompareAndSwapInt64(&flag, 0, 1) {
      count++
      atomic.StoreInt64(&flag, 0)
      return
    }
  }
}
  • 传统的同步原语:互斥锁

var m sync.Mutex
func add() {
  m.Lock()
  count++
  m.Unlock()
}
  • 传统的同步原语:读写锁。适合多读少写场景

type Stat struct {
  counters map[string]int64
  mutex sync.RWMutex
}
func (s *Stat) getCounter(name string) int64 {
  s.mutex.RLock()
  defer s.mutex.RUnlock()
  return s.counters[name]
}
func (s *Stat) SetCounter(name string){
  s.mutex.Lock()
  defer s.mutex.Unlock()
  s.counters[name]++
}
  • 除此之外,Go 语言在传统的同步原语基础上还提供了许多有用的同步工具,包括 sync.Once、sync.Cond、sync.WaitGroup。后面还会介绍到。 好了,今天的分享就到这里了。