MST

星途 面试题库

面试题:Go信号量在高并发场景下的性能优化

在一个高并发的Go应用程序中,使用信号量来控制对共享资源的访问。目前发现随着并发量的增加,性能有所下降。请分析可能的性能瓶颈在哪里,并提出至少两种优化方案,同时写出优化前后的对比代码。
40.0万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能的性能瓶颈分析

  1. 信号量竞争:在高并发场景下,大量的goroutine同时竞争信号量,导致频繁的上下文切换和锁竞争,这会消耗大量的CPU时间。
  2. 资源分配与释放开销:每次获取和释放信号量都有一定的开销,在高并发时,这种开销可能会累积,影响性能。
  3. 共享资源访问序列化:信号量的使用将对共享资源的访问序列化,即使某些操作可以并行执行,也会因为信号量的限制而串行化,降低了并发效率。

优化方案

  1. 增加信号量数量:适当增加信号量的数量,允许更多的goroutine同时访问共享资源,减少竞争。
  2. 分段锁:将共享资源分成多个部分,每个部分使用独立的信号量控制访问,从而减少竞争范围。
  3. 读写锁:如果共享资源读多写少,可以使用读写锁,读操作可以并发执行,写操作时独占资源。

优化前后对比代码

  1. 原始代码(使用单个信号量)
package main

import (
    "fmt"
    "sync"
    "time"
)

var (
    semaphore = make(chan struct{}, 1)
    sharedResource = 0
)

func worker(wg *sync.WaitGroup) {
    semaphore <- struct{}{}
    defer func() { <-semaphore; wg.Done() }()

    // 模拟对共享资源的操作
    sharedResource++
    time.Sleep(time.Millisecond)
    fmt.Println("Worker updated shared resource:", sharedResource)
}

func main() {
    var wg sync.WaitGroup
    numWorkers := 100

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go worker(&wg)
    }

    wg.Wait()
}
  1. 优化方案1:增加信号量数量
package main

import (
    "fmt"
    "sync"
    "time"
)

var (
    semaphore = make(chan struct{}, 10) // 增加信号量数量为10
    sharedResource = 0
)

func worker(wg *sync.WaitGroup) {
    semaphore <- struct{}{}
    defer func() { <-semaphore; wg.Done() }()

    // 模拟对共享资源的操作
    sharedResource++
    time.Sleep(time.Millisecond)
    fmt.Println("Worker updated shared resource:", sharedResource)
}

func main() {
    var wg sync.WaitGroup
    numWorkers := 100

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go worker(&wg)
    }

    wg.Wait()
}
  1. 优化方案2:分段锁(以简单数组分段为例)
package main

import (
    "fmt"
    "sync"
    "time"
)

const (
    numSegments = 10
)

var (
    semaphores = make([]chan struct{}, numSegments)
    sharedArray = make([]int, 100)
)

func init() {
    for i := 0; i < numSegments; i++ {
        semaphores[i] = make(chan struct{}, 1)
    }
}

func worker(wg *sync.WaitGroup, index int) {
    segmentIndex := index / (len(sharedArray) / numSegments)
    semaphores[segmentIndex] <- struct{}{}
    defer func() { <-semaphores[segmentIndex]; wg.Done() }()

    // 模拟对共享数组的操作
    sharedArray[index]++
    time.Sleep(time.Millisecond)
    fmt.Println("Worker updated shared array at index", index, ":", sharedArray[index])
}

func main() {
    var wg sync.WaitGroup
    numWorkers := 100

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go worker(&wg, i)
    }

    wg.Wait()
}
  1. 优化方案3:读写锁
package main

import (
    "fmt"
    "sync"
    "time"
)

var (
    rwMutex sync.RWMutex
    sharedData = 0
)

func reader(wg *sync.WaitGroup) {
    rwMutex.RLock()
    defer func() { rwMutex.RUnlock(); wg.Done() }()

    // 模拟读操作
    fmt.Println("Reader read shared data:", sharedData)
    time.Sleep(time.Millisecond)
}

func writer(wg *sync.WaitGroup) {
    rwMutex.Lock()
    defer func() { rwMutex.Unlock(); wg.Done() }()

    // 模拟写操作
    sharedData++
    fmt.Println("Writer updated shared data:", sharedData)
    time.Sleep(time.Millisecond)
}

func main() {
    var wg sync.WaitGroup
    numReaders := 50
    numWriters := 50

    for i := 0; i < numReaders; i++ {
        wg.Add(1)
        go reader(&wg)
    }

    for i := 0; i < numWriters; i++ {
        wg.Add(1)
        go writer(&wg)
    }

    wg.Wait()
}