MST

星途 面试题库

面试题:Go中sync.Once的典型使用场景有哪些

请举例说明在Go语言开发中,哪些场景适合使用sync.Once来保证某些操作只执行一次,并简要阐述原因。
41.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 初始化全局变量 在Go语言中,有些全局变量的初始化操作开销较大,例如数据库连接池、配置文件加载等。使用 sync.Once 可以确保这些初始化操作只执行一次。
package main

import (
    "fmt"
    "sync"
)

var (
    once     sync.Once
    config   string
)

func loadConfig() {
    config = "loaded config"
    fmt.Println("Config loaded")
}

func getConfig() string {
    once.Do(loadConfig)
    return config
}

原因:在并发环境下,如果不使用 sync.Once,多个协程可能会同时尝试初始化全局变量,导致重复初始化,造成资源浪费或数据不一致。sync.Once 内部通过原子操作和互斥锁保证 loadConfig 函数只被执行一次。

  1. 单例模式实现 在Go语言中实现单例模式时,sync.Once 是一种常用的方式。
package main

import (
    "fmt"
    "sync"
)

type Singleton struct {
    data string
}

var (
    once    sync.Once
    instance *Singleton
)

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{
            data: "Singleton data",
        }
    })
    return instance
}

原因:在并发环境下,常规的单例实现如果不使用 sync.Once 等机制来同步,可能会创建多个实例。sync.Once 确保了无论有多少个协程同时调用 GetInstance 方法,只会创建一个实例。

  1. 延迟初始化资源 对于一些可能在程序运行过程中才需要初始化的资源,并且初始化开销较大,使用 sync.Once 可以在需要时进行初始化,并且保证只初始化一次。
package main

import (
    "fmt"
    "sync"
)

type Resource struct {
    value int
}

var (
    once    sync.Once
    resource *Resource
)

func initializeResource() {
    resource = &Resource{
        value: 42,
    }
    fmt.Println("Resource initialized")
}

func useResource() {
    once.Do(initializeResource)
    fmt.Println("Using resource:", resource.value)
}

原因:在程序启动时就初始化所有资源可能会导致启动时间过长,并且有些资源可能在整个程序运行过程中都不会被使用。使用 sync.Once 可以在真正需要使用资源时才进行初始化,同时保证在并发环境下初始化操作的唯一性。