并发编程
1.Go并发原语
协程Goroutines
每个go程序至少都有一个Goroutine:主Goroutine(在运行进程时自动创建)。以及程序中其他Goroutine 例如:下面程序创建了main的Goroutine及匿名的Goroutine。
func main() {
go func() {
fmt.Println("you forgot me !")
}()
}
在go中有个package是sync,里面包含了:
WaitGroup、Mutex、Cond、Once、Pool。
1.WaitGroup
假设主线程要等待其余的goroutine都运行完毕,不得不在末尾添加time.Sleep(),但是这样会引发两个问题:
等待多长时间?
时间太长,影响性能?
在go的sync库中的WaitGroup可以帮助我们完成此项工作,Add(n)把计数器设置为n,Done()会将计数器每次减1,Wait()函数会阻塞代码运行,直到计数器减0。
等待多个goroutine完成,可以使用一个等待组。
2.Once
sync.Once可以控制函数只能被调用一次,不能多次重复调用。
3.互斥锁Mutex
互斥锁是并发程序对共享资源进行访问控制的主要手段,在go中的sync中提供了Mutex的支持。
4.条件变量Cond
sync.Cond是条件变量,它可以让一系列的 Goroutine 都在满足特定条件时被唤醒。
条件变量通常与互斥锁一起使用,条件变量可以在共享资源的状态变化时通知相关协程。
5.原子操作
原子操作即是进行过程中不能被中断的操作。针对某个值的原子操作在被进行的过程中,CPU绝不会再去进行其他的针对该值的操作。 为了实现这样的严谨性,原子操作仅会由一个独立的CPU指令代表和完成。
在sync/atomic 中,提供了一些原子操作,包括加法(Add)、比较并交换(Compare And Swap,简称 CAS)、加载(Load)、存储(Store)和交换(Swap)。
6.临时对象池Pool
sync.Pool 可以作为临时对象的保存和复用的集合
P是Goroutine中的重要组成之一,例如:P实际上在操作时会为它的每一个goroutine相关的P生成一个本地P。 本地池没有,则会从其它的 P 本地池获取,或者全局P取。
sync.Pool对于需要重复分配、回收内存的地方,sync.Pool 是一个很好的选择。减少GC负担,如果Pool中有对象,下次直接取,不断服用对象内存,减轻 GC 的压力,提升系统的性能。
2. 通道Channel
1.Channel
简单来说是实体之间通过发送消息进行通信,这里发送消息时使用的就是通道,或者叫 Channel。Goroutine对应并发实体。
2.Select
每个case 都必须是一个通信
所有channel表达式都会被求值
如果没有default语句,select将阻塞,直到某个通信可以运行
如果多个case都可以运行,select会随机选择一个执行