面试题答案
一键面试sync.Once基本原理
sync.Once
内部使用一个 done
标志位和一个互斥锁 mu
来实现只执行一次操作的功能。
- 标志位:
done
是一个无符号整型变量,用于标记初始化操作是否已经完成。 - 互斥锁:
mu
是一个互斥锁,用于在多线程环境下保证done
标志位的读写安全。 - 执行逻辑:当第一次调用
Once.Do(f)
时,mu
锁被获取,检查done
标志位,如果done
为 0,表示尚未执行过初始化操作,此时执行传入的函数f
,并将done
设置为 1。后续再调用Once.Do(f)
时,由于done
已经为 1,直接返回,不再执行f
。
常见应用场景举例
- 单例模式:在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
只被调用一次,避免重复初始化数据库连接。