这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
一、 CSP并发模型
CSP模型是上个世纪七十年代提出的,用于描述两个独立的并发实体通过共享的通讯 channel(管道)进行通信的并发模型。 CSP中channel是第一类对象,它不关注发送消息的实体,而关注于发送消息时使用的channel。 GO以CSP模型为理论基础实现并发,主要借用了proccess和channel两个概念
一、porccess
1. 简介
协程相对于线程是很轻量的,线程的栈是mb级别,而协程的栈是kb级别,所以go具有高并发的天然优势,实现百万级别的并发对go来说并不是件难事。线程是内核态的,而协程是用户态的,其调度由用户控制。
二、 channel
1. 产生背景
线程之间进行通信的时候,会因为资源的争夺而产生竟态问题,为了保证数据交换的正确性,必须使用互斥量给内存进行加锁,go语言并发的模型是CSP(顺序通信进程),提倡通过通信共享内存,而不是通过共享内存而实现通信,channel恰巧满足这种需求。
2. 特点
- 用户空间 避免了内核态和用户态的切换导致的成本
- 可以由语言和框架层进行调度
- 更小的栈空间允许创建大量的实例
3. 工作方式
channel类似一个队列,满足先进先出的规则,严格保证收发数据的顺序,每一个通道只能通过固定类型的数据。如果通道进行大型结构体、字符串的传输,可以将对应的指针传进去,尽量的节省空间。
4. 无缓冲与有缓冲的区别
无缓冲是同步的,make(n int),只能向channel发送一个数据,只要这个数据没被接收,之后所有的发送就被阻塞。这时会有报错:fatal error: all goroutines are asleep - deadlock!意为线程陷入了死锁。 有缓冲是异步的,make(n int, 3) 有缓冲是异步的,直到缓存区满了才阻塞。
三、sync
Go语言sync包提供了常见的并发编程同步原语
1. sycn.Mutex
允许在共享资源上互斥访问(不能同时访问)
import "sync"
var (
mu sync.Mutex // guards balance
balance int
)
func Deposit(amount int) {
mu.Lock() // 每次访问balance变量前都会调用mu.Lock获取互斥锁,若有协程已获取了这个锁,操作会阻塞到其它协程调用UnLock
balance = balance + amount
mu.Unlock()
}
func Balance() int {
mu.Lock()
b := balance
mu.Unlock()
return b
}
2. sync.WaitGroup
使用场景:等待goroutine执行完成。
是比time.Sleep更好的写法