Go 并发

Go Memory Model

  • 保证内存顺序满足 happens-before (因果一致性)

GMP

  • G: goroutine, M: thread, P: processor

  • M:N,thread : processor = 1:1

  • 策略

    1. work stealing:空闲 thread 从其他 thread 的 processor 偷取 goroutine
    2. hand off:当前 thread 的 goroutine 阻塞时,将 processor 交给其他空闲 thread

Goroutine

  • go runtime 调度,m:n (m 个 goroutine 对应 n 个 OS 线程)

  • 主协程退出,goroutine 退出

  • Go 高并发

    1. goroutine 栈空间 2KB, 可伸缩
    2. 用户态维护一块内存池,不用系统 malloc,也不用频繁切换内核态
    3. 实现 m:n 调度和上下文切换,减少 goroutine 开销

线程和协程

  • 一个内核线程上可以跑多个协程,抢占式调度
  • 协程有独立的栈空间,共享堆空间,由用户态协作式调度

Channel

  • 用 Channel 实现共享内存,不要用共享内存实现 Channel 😊
  • 队列,遵循 CSP 模型,goroutine 间通信
  • 无缓冲通道需要同时有发送者和接收者,发送时会阻塞,若无接收者会死锁
  • select 多路复用,管理 channel
1
2
3
4
5
6
7
8
9
ch:=make(chan T) // 无缓冲通道
ch:=make(chan T, N) // 有缓冲区
ch:=make(chan <- T) // 单向通道
ch:=make(<- chan T)
ch <- v
v = <- ch
cap(ch)
len(ch)
close(ch)

Context

  • goroutine 之间传递上下文
  • 请求超时退出 goroutine

sync.Mutex

  • 保证只有一个 goroutine 进入临界区访问共享数据,其他 goroutine 等待 mu 释放
  • 不可重入
1
2
3
4
type Mutex struct {
state int32
sema uint32 // 控制锁的信号量
}

sync.RWMutex

  • 读多写少,一个写锁 Lock() 或多个读锁 RLock()
  • 写者优先
1
2
3
4
5
6
7
type RWMutex struct {
w Mutex
writerSem uint32 // writers 信号量
readerSem uint32 // readers 信号量
readerCount int32 // reader 数量
readerWait int32 // writer 申请锁时,已有锁的 reader 数量
}

sync.WaitGroup

  • 主协程同步 goroutine 任务
  • WaitGroup 是结构体,指针传递
name func
(wg * WaitGroup) Add(n int) counter + n
(wg *WaitGroup) Done() counter - 1
(wg *WaitGroup) Wait() 阻塞至 counter = 0

sync.Once

  • 一次性操作

sync.Cond

  • multi-goroutine 间的通知
  • cond 维护 goroutine 等待队列
  • (&c).Wait():cond 加锁时用
  • (&c).Signal(): 唤醒并移除 cond 队列第一个 goroutine
  • (&c).Broadcast(): 唤醒并移除 cond 队列所有 goroutine
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func main() {
const N = 10
var values [N]string

cond := sync.NewCond(&sync.Mutex{})

for i := 0; i < N; i++ {
go func(i int) {
time.Sleep(100*time.Millisecond)
cond.L.Lock()
values[i] = string('[goroutine]' + i)
cond.Broadcast()
cond.L.Unlock()
}(i)
}
check:= func() bool {
for i := 0; i < N; i++ {
if values[i] == "" {
return false
}
}
return true
}

cond.L.Lock()
defer cond.L.Unlock()
for !check() {
cond.Wait()
}
}

cover
画师: ノーコピーライトガール
id: 122225604