优秀的编程知识分享平台

网站首页 > 技术文章 正文

对比着学 Go 语言-进阶:并发的 3 种应对思路

nanyue 2024-12-01 01:43:17 技术文章 6 ℃

对并发的处理还有 3 种思路:

  • 多核并行化
  • 出让时间片
  • 同步

多核并行化

通过了解 CPU 核心数量,并针对性地分解计算任务到多个 goroutine 中并行计算。

type Vector []float64

func (v Vector) DoSome(i, n int, u Vector, c chan int) {
     for ; i < n; i++ {
        v[i] += u.Op(v[i]) 
     }
     c <- 1 // 发信号告诉任务管理者已经计算完成
}

const NCPU = 16

func (v Vector) DoAll(u Vector) {
     c := make(chan int, NCPU) // 用于接收每个 CPU 的任务完成信号
  
  	 for i := 0; i < NCPU; i++ {
         go v.DoSome(i*len(v)/NCPU, (i+1)*len(v)/NCPU, u, c) 
     }
  
  	// 等待所有 CPU 的任务完成
    for i := 0; i<NCPU; i++ {
        <-c // 获取到一个数据,表示一个 CPU 计算完成了 
    }
  
  // 到这里表示所有计算已经结束
}

目前还需要通过设置环境变量 GOMAXPROCS 的值来控制使用多少个 CPU 核心。

// 在代码启动 goroutine 之前调用
runtime.GOMAXPROCS(16)

可以通过 runtime.NumCPU() 来获取核心数。

出让时间片

这涉及到比较精细的控制 goroutine 的行为,需要比较深入地了解 Go 中的 runtime 包提供的具体功能。

同步

同步锁

除了使用 channel ,Go 也提供资源锁的方案。

包括 2 种类型的锁:

  • sync.Mutex
  • sync.RWMutex

当一个 goroutine 获得了 Mutex 之后,其他的 goroutine 就只能等待这个 goroutine 释放此 Mutex。

RWMutex 是经典的单写多读模型。在读锁占用的情况下,会阻止写,但不阻止读;在写锁占用时,会阻止所有的读写。

从数据结构上来看,RWMutex 组合了 Mutex:

type RWMutex struct {
    w Mutex
    writerSem uint32
    readerSem uint32
    readerCount int32
    readerWait int32
}

锁的基本使用是,要保证上锁和解锁的对应。标准的锁用法如下:

var l sync.Mutex
func foo() {
   l.Lock()
   defer l.Unlock()
}

全局唯一操作

对于从全局角度只需要运行一次的代码:

  • 全局初始化

Go 中提供 Once 类型来保证全局的唯一操作。

var a string
var once sync.Once

func setup() {
   a = "hello, world" 
}

func doprint() {
  once.Do(setup)
  print(a)
}

func twoprint() {
  go doprint()
  go doprint()
}

Tags:

最近发表
标签列表