Go语言的并发模型
Go语言实现了一下两种并发形式:
第一种是大家普遍认知的:多线程共享内存。其实就许多主流编程语言中的多线程开发。
另外一种是Go语言特有的,也是Go语言推荐的:CSP(communicating sequential processes)并发模型。该方式是Go语言最大的两个亮点goroutine和chan,二者合体的典型应用。
什么是CSP
CSP 是 Communicating Sequential Process 的简称,中文可以叫做通信顺序进程,是一种并发编程模型,是一个很强大的并发数据模型,是上个世纪七十年代提出的,用于描述两个独立的并发实体通过共享的通讯 channel(管道)进行通信的并发模型。
Go语言其实只用到了 CSP 的很小一部分,即理论中的 Process/Channel(对应到语言中的 goroutine/channel):这两个并发原语之间没有从属关系, Process 可以订阅任意个 Channel,Channel 也并不关心是哪个 Process 在利用它进行通信;Process 围绕 Channel 进行读写,形成一套有序阻塞和可预测的并发模型。
相信大家一定见过一句话:
Do not communicate by sharing memory; instead, share memory by communicating.
不要通过共享内存来通信,而要通过通信来实现内存共享。
这就是 Go 的并发哲学,它依赖 CSP 模型,基于 channel 实现。
Mutex & CSP 实现方式对比
sync.Mutex
var lock sync.Mutex
var wg sync.WaitGroup
var m1 map[int]int
var m2 map[int]int
func update_map_by_mutex(i int) {
defer lock.Unlock() //解锁
lock.Lock() //上锁
m1[i] = i //赋值
wg.Done()
}
func main() {
m1 = make(map[int]int, max)
m2 = make(map[int]int, max)
for i := 0; i < 1000; i++ { //起1000个协程,通过加锁的方式保证安全并发
wg.Add(1)
go update_map_by_mutex(i)
}
wg.Wait()
}
csp
var m1 map[int]int
var m2 map[int]int
type Op struct {
key int
val int
}
var ch chan Op
var wg sync.WaitGroup
func update_map_by_chan(i int) { //往channel中塞数据
ch <- Op{key: i, val: i}
wg.Done()
}
func wait_for_chan(m map[int]int) { //从channel中读取数据并处理
for {
if op,ok := <-ch;ok{
m[op.key] = op.val
} else {
break
}
}
wg.Done()
}
func main() {
m1 = make(map[int]int, max)
m2 = make(map[int]int, max)
ch = make(chan Op)
wg.Add(1)
go wait_for_chan(m2)
for i := 0; i < max; i++ {
wg.Add(1)
go update_map_by_chan(i)
}
wg.Wait()
}
小结
虽然csp是go更为推荐的并发模型,但还是需要根据实际情况酌情选用,切勿过度使用!