MST

星途 面试题库

面试题:Go每个请求一个goroutine的并发控制与性能优化

假设你正在处理高并发请求,每个请求对应一个goroutine。在这种情况下,如何通过合理的并发控制(如使用sync包中的工具)来避免资源竞争,同时提高整体性能?请结合具体代码示例说明。
17.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

在Go语言中处理高并发请求并避免资源竞争,同时提高整体性能,可以使用sync包中的多个工具,以下以sync.Mutexsync.WaitGroup为例进行说明:

  1. 使用sync.Mutex避免资源竞争
    • sync.Mutex用于保护共享资源,确保同一时间只有一个goroutine可以访问该资源。
    • 示例代码如下:
package main

import (
    "fmt"
    "sync"
)

var (
    counter int
    mu      sync.Mutex
)

func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    mu.Lock()
    counter++
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    numRoutines := 1000
    for i := 0; i < numRoutines; i++ {
        wg.Add(1)
        go increment(&wg)
    }
    wg.Wait()
    fmt.Printf("Final counter value: %d\n", counter)
}

在上述代码中,mu是一个sync.Mutex实例。在increment函数中,我们使用mu.Lock()锁定互斥锁,这样在同一时间只有一个goroutine可以执行counter++操作,从而避免了资源竞争。执行完对共享资源counter的操作后,通过mu.Unlock()解锁互斥锁。

  1. 使用sync.WaitGroup等待所有goroutine完成

    • sync.WaitGroup用于等待一组goroutine完成。
    • 在上述代码中,wg是一个sync.WaitGroup实例。在启动每个goroutine前,使用wg.Add(1)增加等待组的计数。在每个goroutine结束时,使用defer wg.Done()减少等待组的计数。在main函数中,通过wg.Wait()阻塞,直到所有goroutine完成(即等待组的计数为0)。
  2. 使用sync.Map提高性能(适用于某些场景)

    • 如果需要在高并发环境下使用map类型的数据结构,sync.Map是一个更好的选择,它不需要像普通map那样手动加锁来避免资源竞争。
    • 示例代码如下:
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    var m sync.Map
    numRoutines := 1000
    for i := 0; i < numRoutines; i++ {
        wg.Add(1)
        go func(key int) {
            defer wg.Done()
            m.Store(key, key*2)
        }(i)
    }
    wg.Wait()
    m.Range(func(key, value interface{}) bool {
        fmt.Printf("Key: %d, Value: %d\n", key, value)
        return true
    })
}

在这个例子中,sync.MapStoreRange方法内部已经处理了并发安全问题,不需要手动加锁解锁,从而在高并发场景下提高了性能。

通过合理使用sync.Mutexsync.WaitGroup以及sync.Map等工具,可以有效地在高并发环境下避免资源竞争,同时提高整体性能。