MST

星途 面试题库

面试题:Go函数的并发安全与优化

假设你有一个函数,会被多个并发的Go协程调用,该函数内部操作共享资源,例如对一个全局变量进行累加操作。1. 描述如何保证该函数在并发环境下的安全性。2. 从性能优化角度出发,阐述如何在保证并发安全的前提下,尽量提升该函数的执行效率。请结合代码示例说明。
45.7万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 保证并发安全
    • 在Go语言中,可以使用sync.Mutex(互斥锁)来保证对共享资源的安全访问。互斥锁可以确保在同一时间只有一个协程能够访问共享资源。
    • 示例代码如下:
package main

import (
    "fmt"
    "sync"
)

var (
    counter int
    mu      sync.Mutex
)

func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    mu.Lock()
    counter++
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    numRoutines := 10
    for i := 0; i < numRoutines; i++ {
        wg.Add(1)
        go increment(&wg)
    }
    wg.Wait()
    fmt.Println("Final counter value:", counter)
}

在上述代码中,mu是一个sync.Mutex实例。在对counter这个共享资源进行累加操作前,先调用mu.Lock()获取锁,操作完成后调用mu.Unlock()释放锁,这样就保证了在同一时间只有一个协程可以修改counter,从而保证了并发安全。

  1. 性能优化
    • 减少锁的粒度:如果可能,尽量缩小锁保护的代码范围,只对真正需要保护的共享资源操作加锁。例如,如果在对全局变量累加操作前后还有其他不需要保护的操作,可以将这些操作放在锁的外面。
    • 使用sync.RWMutex(读写互斥锁):如果读操作远多于写操作,可以使用读写互斥锁。读操作时多个协程可以同时进行,只有写操作需要独占锁。
    • 示例代码(使用sync.RWMutex优化读多写少场景):
package main

import (
    "fmt"
    "sync"
)

var (
    data  int
    rwmu  sync.RWMutex
)

func readData(wg *sync.WaitGroup) {
    defer wg.Done()
    rwmu.RLock()
    fmt.Println("Read data:", data)
    rwmu.RUnlock()
}

func writeData(wg *sync.WaitGroup) {
    defer wg.Done()
    rwmu.Lock()
    data++
    rwmu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    numReaders := 5
    numWriters := 2
    for i := 0; i < numReaders; i++ {
        wg.Add(1)
        go readData(&wg)
    }
    for i := 0; i < numWriters; i++ {
        wg.Add(1)
        go writeData(&wg)
    }
    wg.Wait()
}

在这个示例中,读操作使用rwmu.RLock()获取读锁,允许多个协程同时读,写操作使用rwmu.Lock()获取写锁,保证写操作的原子性,从而在保证并发安全的前提下提升了性能,尤其在读多写少的场景下。