MST
星途 面试题库

面试题:Go类型方法优化与并发场景

在并发编程场景下,一个Go类型的方法可能会被多个goroutine同时调用。请详细说明如何对这种类型方法进行优化,以避免数据竞争和提高并发性能,包括但不限于使用sync包中的工具,以及如何设计方法结构来更好地适应并发环境。
10.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 使用sync.Mutex
    • 当方法涉及对共享数据的读写操作时,使用sync.Mutex来保护这些操作。
    • 示例:
type Counter struct {
    value int
    mu    sync.Mutex
}

func (c *Counter) Increment() {
    c.mu.Lock()
    c.value++
    c.mu.Unlock()
}

func (c *Counter) GetValue() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.value
}
  1. 使用sync.RWMutex
    • 如果读操作远多于写操作,可以使用sync.RWMutex。读操作时使用RLock,写操作时使用Lock
    • 示例:
type Data struct {
    data  map[string]interface{}
    rwmu  sync.RWMutex
}

func (d *Data) Read(key string) interface{} {
    d.rwmu.RLock()
    defer d.rwmu.RUnlock()
    return d.data[key]
}

func (d *Data) Write(key string, value interface{}) {
    d.rwmu.Lock()
    defer d.rwmu.Unlock()
    if d.data == nil {
        d.data = make(map[string]interface{})
    }
    d.data[key] = value
}
  1. 使用sync.Cond
    • 当需要在某个条件满足时唤醒等待的goroutine,可以使用sync.Cond
    • 示例:
type Queue struct {
    data  []interface{}
    mu    sync.Mutex
    cond  *sync.Cond
}

func NewQueue() *Queue {
    q := &Queue{}
    q.cond = sync.NewCond(&q.mu)
    return q
}

func (q *Queue) Enqueue(item interface{}) {
    q.mu.Lock()
    q.data = append(q.data, item)
    q.cond.Broadcast()
    q.mu.Unlock()
}

func (q *Queue) Dequeue() interface{} {
    q.mu.Lock()
    for len(q.data) == 0 {
        q.cond.Wait()
    }
    item := q.data[0]
    q.data = q.data[1:]
    q.mu.Unlock()
    return item
}
  1. 设计方法结构适应并发环境
    • 减少共享数据:尽量让每个goroutine有自己独立的数据,避免共享数据带来的竞争。例如,使用context.Context传递请求特定的数据,而不是在全局共享。
    • 使用通道(Channel):通过通道进行goroutine间的通信和同步,避免直接共享数据。例如,设计一个生产者 - 消费者模型,生产者将数据发送到通道,消费者从通道接收数据。
func producer(ch chan<- int) {
    for i := 0; i < 10; i++ {
        ch <- i
    }
    close(ch)
}

func consumer(ch <-chan int) {
    for value := range ch {
        // 处理数据
    }
}
- **分段锁**:对于大型数据结构,可以使用分段锁。例如,在一个大的哈希表中,为不同的哈希桶设置不同的锁,这样可以减少锁争用。
type ShardedMap struct {
    shards []shard
}

type shard struct {
    data map[string]interface{}
    mu   sync.Mutex
}

func NewShardedMap(numShards int) *ShardedMap {
    sm := &ShardedMap{
        shards: make([]shard, numShards),
    }
    for i := range sm.shards {
        sm.shards[i].data = make(map[string]interface{})
    }
    return sm
}

func (sm *ShardedMap) Get(key string) interface{} {
    index := int(hash(key)) % len(sm.shards)
    sm.shards[index].mu.Lock()
    defer sm.shards[index].mu.Unlock()
    return sm.shards[index].data[key]
}

func (sm *ShardedMap) Set(key string, value interface{}) {
    index := int(hash(key)) % len(sm.shards)
    sm.shards[index].mu.Lock()
    defer sm.shards[index].mu.Unlock()
    sm.shards[index].data[key] = value
}

这里hash函数是自定义的哈希函数,用于将键映射到合适的分片。