Go 并发同步中的 runtime 相关知识
在 Go 语言中,runtime 包负责管理 goroutine 的调度、内存分配、垃圾回收等底层功能。了解 Go 运行时中的一些关键概念和机制,有助于编写高效的并发程序,并深入理解 Go 的并发模型。
1. Goroutine 调度
Goroutine 是 Go 中的轻量级线程,由 Go runtime 管理。Goroutine 的调度由 Go 的 M:N 调度器实现,其中 M 代表操作系统线程,N 代表 goroutine。
- Goroutine: 每个 Go 程序启动时都会创建一个主 goroutine。
- M(Machine): 代表操作系统线程。
- P(Processor): 表示逻辑处理器,负责执行 goroutine。P 的数量由
GOMAXPROCS
环境变量控制。 - G(Goroutine): 代表 goroutine,多个 goroutine 可以在一个 P 上执行。
调度器会将 goroutine 分配到不同的 P 上执行,P 再分配到 M 上,实际运行在操作系统线程中。
2. GMP 模型
GMP 模型是 Go runtime 用于管理并发的核心机制:
- G(Goroutine): Go runtime 管理的 goroutine。
- M(Machine): 操作系统线程。
- P(Processor): 逻辑处理器,调度 goroutine 到 M 上。
P 的数量等于 GOMAXPROCS
的值,表示可以同时运行的 goroutine 数量。Go runtime 通过 GMP 模型有效地管理和调度 goroutine,实现高效并发。
3. 内存同步
Go 提供了多种原语来进行内存同步,保证多 goroutine 间的内存可见性和一致性。
- sync.Mutex: 互斥锁,用于保护共享资源,保证同一时刻只有一个 goroutine 访问。
- sync.RWMutex: 读写锁,允许多个读操作并发执行,但写操作是独占的。
- sync.WaitGroup: 等待组,用于等待一组 goroutine 完成。
- sync.Cond: 条件变量,用于 goroutine 的阻塞和唤醒。
- sync.Once: 一次性操作,确保某个操作只执行一次。
- sync/atomic: 原子操作,用于对单个变量进行原子操作,避免使用锁的开销。
4. Go runtime 相关函数
runtime.GOMAXPROCS(n int) int
: 设置可以并行执行的最大 goroutine 数量。runtime.Gosched()
: 让出当前 goroutine 的执行权,调度器会重新调度其他 goroutine。runtime.Goexit()
: 退出当前 goroutine,不影响其他 goroutine 的执行。runtime.NumCPU() int
: 返回当前系统的 CPU 数量。runtime.NumGoroutine() int
: 返回当前正在运行的 goroutine 数量。
示例代码
以下是一些示例代码,演示如何使用上述同步原语和 runtime 相关函数:
Goroutine 调度和 GOMAXPROCS 示例
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
// 设置最大并行执行的 goroutine 数量
runtime.GOMAXPROCS(4)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("Goroutine %d running\n", id)
}(i)
}
wg.Wait()
fmt.Println("All goroutines finished")
}
使用 Mutex 和 WaitGroup 进行内存同步
package main
import (
"fmt"
"sync"
)
var (
counter int
mu sync.Mutex
wg sync.WaitGroup
)
func increment() {
defer wg.Done()
for i := 0; i < 1000; i++ {
mu.Lock()
counter++
mu.Unlock()
}
}
func main() {
for i := 0; i < 10; i++ {
wg.Add(1)
go increment()
}
wg.Wait()
fmt.Println("Final Counter:", counter)
}
使用原子操作进行内存同步
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var counter int32
var wg sync.WaitGroup
func increment() {
defer wg.Done()
for i := 0; i < 1000; i++ {
atomic.AddInt32(&counter, 1)
}
}
func main() {
for i := 0; i < 10; i++ {
wg.Add(1)
go increment()
}
wg.Wait()
fmt.Println("Final Counter:", counter)
}
总结
理解 Go runtime 中的并发和同步机制是编写高效并发程序的关键。Goroutine 调度、GMP 模型、内存同步原语和 runtime 相关函数是 Go 并发编程的重要组成部分。通过合理使用这些工具,可以实现高效、安全的并发程序。