你好,我是一名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。后面还会介绍到。 好了,今天的分享就到这里了。