Go语言中并发模型

32 阅读2分钟

CSP(Communicating Sequential Processes)并发模型

CSP 模型的核心思想是 “不要通过共享内存来通信,而是通过通信来共享内存” 。 Go 通过 goroutinechannel 来实现 CSP 并发。

Goroutine(协程)

Goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理,创建成本远低于系统线程。

  • go 关键字用于启动新协程。
  • Goroutine 由 Go 运行时调度,而非 OS 线程,调度器会在多个线程上执行它们。
  • Goroutine 是非阻塞的,主 Goroutine 可能会先退出,导致其他 Goroutine 也终止。

Channel(通道)

用于 Goroutine 之间的通信和数据同步。

无缓冲通道(同步)

  • 无缓冲通道会阻塞 发送方,直到 接收方 读取数据。

带缓冲通道(异步)

  • 发送数据到缓冲未满的通道时不会阻塞。
  • 当通道满时,发送操作会阻塞,直到有 Goroutine 读取数据。 注:
  • 关闭的通道不能再发送数据,否则会导致 panic。
  • 读取已关闭通道的数据,若通道为空,则会返回零值。

WaitGroup(等待所有 Goroutine 完成)

sync.WaitGroup 用于等待多个 Goroutine 结束。

  • wg.Add(n) 指定等待的任务数量。
  • wg.Done() 表示一个任务完成。
  • wg.Wait() 阻塞主 Goroutine,直到所有任务完成。

Context

context 在 Go 并发编程中主要用于 控制 Goroutine 的生命周期,严格来说,它并不完全属于 CSP 并发模型或共享内存并发模型,而是 一种协作取消机制,但它更符合 CSP 并发模型 的思想。

  • 使用 context.WithTimeout 实现超时广播。
  • 使用 context.WithDeadline指定了一个绝对时间点,从而实现超时。
  • context.WithCancel() 适用于手动触发广播,发送取消信号,所有 Goroutine 退出。

共享内存(Shared Memory)并发模型

共享内存模型的核心思想是 多个 Goroutine 通过访问同一个变量进行通信,并使用同步机制(如 互斥锁(Mutex)读写锁(RWMutex) )来保证数据安全。

互斥锁

在并发环境下,多个 Goroutine 访问共享资源时需要同步,sync.Mutex 用于保证资源互斥访问。

  • mutex.Lock() 互斥访问共享资源。
  • mutex.Unlock() 释放锁,避免死锁。

读写锁

sync.RWMutex 允许多个 Goroutine 同时读取,但 写入时必须互斥

  • RLock() 允许多个读操作同时进行。
  • Lock() 只允许一个写操作,写入时会阻塞所有读操作。

条件变量

sync.Cond 用于协调 Goroutine 之间的等待和唤醒。

  • cond.Wait() 让 Goroutine 等待条件满足。
  • cond.Broadcast() 唤醒所有等待的 Goroutine。

原子操作

  • 直接 修改共享变量,但保证操作的不可分割性(Atomicity)。
  • 不使用锁,但仍然是 基于共享内存 的并发控制机制。
  • 典型操作:
    • atomic.AddInt64(&count, 1) 原子递增
    • atomic.LoadInt64(&count) 原子读取
    • atomic.CompareAndSwapInt64(&value, old, new) 原子 CAS
  • 这些操作本质上都是 在共享内存上进行无锁同步