MST
星途 面试题库

面试题:Go中sync.Once的基本原理及应用场景

请简述Go语言中sync.Once的基本原理,并举例说明它在实际项目中的常见应用场景。
30.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

sync.Once基本原理

sync.Once 内部使用一个 done 标志位和一个互斥锁 mu 来实现只执行一次操作的功能。

  1. 标志位done 是一个无符号整型变量,用于标记初始化操作是否已经完成。
  2. 互斥锁mu 是一个互斥锁,用于在多线程环境下保证 done 标志位的读写安全。
  3. 执行逻辑:当第一次调用 Once.Do(f) 时,mu 锁被获取,检查 done 标志位,如果 done 为 0,表示尚未执行过初始化操作,此时执行传入的函数 f,并将 done 设置为 1。后续再调用 Once.Do(f) 时,由于 done 已经为 1,直接返回,不再执行 f

常见应用场景举例

  1. 单例模式:在Go语言中实现单例模式非常方便。例如:
package main

import (
    "fmt"
    "sync"
)

type Singleton struct {
    Data string
}

var instance *Singleton
var once sync.Once

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

在上述代码中,无论多少个 goroutine 同时调用 GetInstance 函数,instance 只会被初始化一次。 2. 初始化资源:在应用启动时,可能需要初始化一些共享资源,如数据库连接池、配置文件加载等,只需要执行一次。例如:

package main

import (
    "database/sql"
    "fmt"
    "sync"

    _ "github.com/go-sql-driver/mysql"
)

var db *sql.DB
var once sync.Once

func InitDB() {
    var err error
    db, err = sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/test")
    if err != nil {
        panic(err)
    }
}

func GetDB() *sql.DB {
    once.Do(InitDB)
    return db
}

在这个例子中,InitDB 函数用于初始化数据库连接,GetDB 函数通过 once.Do 确保 InitDB 只被调用一次,避免重复初始化数据库连接。