MST
星途 面试题库

面试题:Go函数在并发场景下边界情况的综合处理

假设你正在编写一个Go程序,其中有一个函数会在多个goroutine中并发调用,该函数接收一个共享的资源(如一个结构体)作为参数,并且需要对这个共享资源进行读写操作。请描述并实现如何处理诸如资源竞争、数据不一致等边界情况,同时要考虑在高并发下的性能优化。
42.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

在Go语言中处理共享资源的并发读写问题,通常使用sync包中的工具,比如Mutex(互斥锁)、RWMutex(读写互斥锁)来避免资源竞争和数据不一致问题。以下是具体的描述和实现示例:

1. 使用 Mutex 处理简单的读写操作

如果读写操作都需要独占访问共享资源,可以使用 MutexMutex 会阻止多个 goroutine 同时进入临界区(对共享资源进行操作的代码块),从而避免资源竞争。

package main

import (
    "fmt"
    "sync"
)

// 定义共享资源结构体
type SharedResource struct {
    data int
}

// 定义一个函数,在多个 goroutine 中并发调用
func worker(resource *SharedResource, mu *sync.Mutex, wg *sync.WaitGroup) {
    defer wg.Done()
    // 锁定互斥锁
    mu.Lock()
    // 对共享资源进行读写操作
    resource.data++
    fmt.Printf("Goroutine modified data to: %d\n", resource.data)
    // 解锁互斥锁
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    resource := SharedResource{data: 0}
    mu := sync.Mutex{}

    // 启动多个 goroutine
    numGoroutines := 5
    for i := 0; i < numGoroutines; i++ {
        wg.Add(1)
        go worker(&resource, &mu, &wg)
    }

    // 等待所有 goroutine 完成
    wg.Wait()
}

2. 使用 RWMutex 优化读多写少的场景

如果读操作远远多于写操作,可以使用 RWMutexRWMutex 允许多个 goroutine 同时进行读操作,但写操作时需要独占访问。

package main

import (
    "fmt"
    "sync"
)

// 定义共享资源结构体
type SharedResource struct {
    data int
}

// 定义一个读函数,在多个 goroutine 中并发调用
func reader(resource *SharedResource, mu *sync.RWMutex, wg *sync.WaitGroup) {
    defer wg.Done()
    // 锁定读锁
    mu.RLock()
    // 对共享资源进行读操作
    fmt.Printf("Goroutine read data: %d\n", resource.data)
    // 解锁读锁
    mu.RUnlock()
}

// 定义一个写函数,在多个 goroutine 中并发调用
func writer(resource *SharedResource, mu *sync.RWMutex, wg *sync.WaitGroup) {
    defer wg.Done()
    // 锁定写锁
    mu.Lock()
    // 对共享资源进行写操作
    resource.data++
    fmt.Printf("Goroutine modified data to: %d\n", resource.data)
    // 解锁写锁
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    resource := SharedResource{data: 0}
    mu := sync.RWMutex{}

    // 启动多个读 goroutine
    numReaders := 3
    for i := 0; i < numReaders; i++ {
        wg.Add(1)
        go reader(&resource, &mu, &wg)
    }

    // 启动多个写 goroutine
    numWriters := 2
    for i := 0; i < numWriters; i++ {
        wg.Add(1)
        go writer(&resource, &mu, &wg)
    }

    // 等待所有 goroutine 完成
    wg.Wait()
}

3. 性能优化

  • 减少锁的粒度:如果可能,将共享资源拆分成多个部分,每个部分使用单独的锁,这样不同的 goroutine 可以同时操作不同部分的资源,提高并发性能。
  • 读写分离:在读写操作频繁的场景下,使用 RWMutex 可以显著提高性能,因为读操作可以并发执行。
  • 避免不必要的锁操作:尽量将不需要锁保护的代码移出临界区,减少锁的持有时间,从而提高并发性能。

通过合理使用这些技术,可以有效处理共享资源在高并发下的资源竞争和数据不一致问题,并优化程序的性能。