MST

星途 面试题库

面试题:Go语言sync.Once在并发服务启动场景下的优化应用

在一个高并发的Go服务启动过程中,多个协程可能同时尝试初始化某些共享资源(如数据库连接池、缓存客户端等),使用sync.Once虽然能保证只初始化一次,但可能存在性能瓶颈。请分析可能出现性能瓶颈的原因,并提出至少一种优化方案,同时用代码示例展示优化后的实现。
27.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

性能瓶颈原因分析

sync.Once 的性能瓶颈主要在于其内部使用了互斥锁 Mutex。在高并发场景下,多个协程同时尝试执行初始化操作时,由于 Mutex 的存在,只有一个协程能够进入临界区执行初始化代码,其他协程都需要等待锁的释放。这就导致大量协程处于阻塞状态,增加了协程调度开销,降低了系统的并发性能。

优化方案

  1. 懒汉式初始化:只有在真正需要使用共享资源时才进行初始化,并且在初始化时使用双重检查锁定(Double-Check Locking)机制来减少锁的竞争。
  2. 预初始化:在服务启动时,提前初始化好共享资源,避免在高并发阶段进行初始化操作。

代码示例(懒汉式初始化)

package main

import (
    "fmt"
    "sync"
)

type Resource struct {
    // 这里可以定义资源的具体结构,比如数据库连接池的配置等
    data string
}

var (
    resourceInstance *Resource
    once             sync.Once
    mu               sync.Mutex
)

func GetResource() *Resource {
    if resourceInstance != nil {
        return resourceInstance
    }
    mu.Lock()
    defer mu.Unlock()
    if resourceInstance == nil {
        resourceInstance = &Resource{
            data: "初始化的数据",
        }
    }
    return resourceInstance
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            res := GetResource()
            fmt.Println(res.data)
        }()
    }
    wg.Wait()
}

代码示例(预初始化)

package main

import (
    "fmt"
    "sync"
)

type Resource struct {
    // 这里可以定义资源的具体结构,比如数据库连接池的配置等
    data string
}

var resourceInstance *Resource

func init() {
    resourceInstance = &Resource{
        data: "预初始化的数据",
    }
}

func GetResource() *Resource {
    return resourceInstance
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            res := GetResource()
            fmt.Println(res.data)
        }()
    }
    wg.Wait()
}