Go语言的并发模型

2,437 阅读2分钟

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更为推荐的并发模型,但还是需要根据实际情况酌情选用,切勿过度使用!