MST

星途 面试题库

面试题:在高并发场景下Go语言切片扩容可能会遇到什么问题及如何优化

假设在一个高并发的Go程序中频繁对切片进行操作,切片扩容机制可能引发哪些并发安全问题、资源竞争问题等,针对这些问题,应如何从设计层面、代码实现层面进行优化,以保障程序的稳定性和高性能。
32.1万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能引发的问题

  1. 并发安全问题:多个协程同时对切片进行读写操作时,可能导致数据不一致。例如,一个协程正在读取切片中的某个元素,而另一个协程同时对切片进行扩容并修改了该元素,这就会导致读取到的数据不正确。
  2. 资源竞争问题:在高并发环境下,多个协程可能同时尝试对切片进行扩容。由于切片的扩容涉及内存的重新分配和数据的复制,这可能导致资源竞争,例如多个协程同时申请内存,可能造成内存分配混乱,进而影响程序性能和稳定性。

设计层面优化

  1. 使用sync包:可以利用sync.Mutexsync.RWMutex来保护对切片的操作。对于读多写少的场景,sync.RWMutex更合适,它允许多个读操作同时进行,但写操作时会独占切片,防止其他读写操作。
  2. 数据分片:将数据按照一定规则分片存储在多个切片中,不同协程操作不同的切片,减少竞争。例如,按照哈希值将数据分配到不同切片,每个协程负责处理特定哈希值范围内的数据。

代码实现层面优化

  1. 使用sync.Mutex示例
package main

import (
    "fmt"
    "sync"
)

var (
    slice []int
    mu    sync.Mutex
)

func addToSlice(num int) {
    mu.Lock()
    slice = append(slice, num)
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            addToSlice(n)
        }(i)
    }
    wg.Wait()
    fmt.Println(slice)
}
  1. 使用sync.RWMutex示例
package main

import (
    "fmt"
    "sync"
)

var (
    slice []int
    rwmu  sync.RWMutex
)

func readSlice() {
    rwmu.RLock()
    fmt.Println(slice)
    rwmu.RUnlock()
}

func writeSlice(num int) {
    rwmu.Lock()
    slice = append(slice, num)
    rwmu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            writeSlice(n)
        }(i)
    }
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            readSlice()
        }()
    }
    wg.Wait()
}
  1. 数据分片示例
package main

import (
    "fmt"
    "sync"
)

const numShards = 10

var (
    shards  [numShards][]int
    mu      [numShards]sync.Mutex
)

func addToShard(num int) {
    shardIndex := num % numShards
    mu[shardIndex].Lock()
    shards[shardIndex] = append(shards[shardIndex], num)
    mu[shardIndex].Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            addToShard(n)
        }(i)
    }
    wg.Wait()
    for i := 0; i < numShards; i++ {
        fmt.Printf("Shard %d: %v\n", i, shards[i])
    }
}