面试题答案
一键面试性能瓶颈
- 信号量竞争:高并发时,多个 goroutine 频繁获取和释放信号量可能导致激烈竞争,增加锁的开销,降低性能。
- 资源耗尽:如果信号量允许的最大资源数设置不合理,可能导致资源被过度使用,最终耗尽,使后续请求等待。
- 阻塞等待开销:当信号量资源不足时,goroutine 进入阻塞等待状态,频繁的阻塞和唤醒操作会带来额外的系统开销。
优化方向
- 数据结构优化
- 采用更高效的队列:如果需要管理等待信号量的 goroutine,使用高效的队列数据结构,如循环队列,减少插入和删除操作的时间复杂度。
- 资源管理优化
- 动态调整信号量资源:根据系统负载动态调整信号量允许的最大资源数,避免资源过度使用或闲置。
- 资源预分配:提前分配一定数量的资源,减少运行时的资源分配开销。
- 调度优化
- 优先调度策略:为不同优先级的任务设置不同的信号量获取策略,优先满足高优先级任务。
代码示例
package main
import (
"fmt"
"sync"
"time"
)
var semaphore = make(chan struct{}, 5) // 信号量,最多允许5个goroutine同时执行
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
semaphore <- struct{}{} // 获取信号量
defer func() { <-semaphore }() // 释放信号量
fmt.Printf("Worker %d started\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d finished\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 10; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
}
上述代码展示了一个简单的信号量使用示例。可以通过以下方式优化:
- 动态调整信号量资源
package main
import (
"fmt"
"sync"
"time"
)
var semaphore chan struct{}
func adjustSemaphore(capacity int) {
semaphore = make(chan struct{}, capacity)
}
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
semaphore <- struct{}{}
defer func() { <-semaphore }()
fmt.Printf("Worker %d started\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d finished\n", id)
}
func main() {
var wg sync.WaitGroup
adjustSemaphore(5)
for i := 1; i <= 10; i++ {
wg.Add(1)
go worker(i, &wg)
}
// 根据系统负载动态调整信号量容量
go func() {
time.Sleep(3 * time.Second)
adjustSemaphore(10)
}()
wg.Wait()
}
- 使用高效队列管理等待的 goroutine(示例较为复杂,此处简单示意思路):可以实现一个基于循环队列的等待队列,在获取信号量时,如果信号量不足,将 goroutine 加入队列,释放信号量时,从队列中唤醒等待的 goroutine。
// 简单示意循环队列结构
type CircularQueue struct {
buffer []int
head int
tail int
}
func NewCircularQueue(capacity int) *CircularQueue {
return &CircularQueue{
buffer: make([]int, capacity),
head: 0,
tail: 0,
}
}
func (cq *CircularQueue) Enqueue(item int) {
cq.buffer[cq.tail] = item
cq.tail = (cq.tail + 1) % len(cq.buffer)
}
func (cq *CircularQueue) Dequeue() int {
item := cq.buffer[cq.head]
cq.head = (cq.head + 1) % len(cq.buffer)
return item
}
在信号量获取和释放逻辑中结合这个队列结构,优化等待 goroutine 的管理。