sync.Once 用于确保某个函数只执行一次,无论有多少个 goroutine 尝试执行它。它有一个方法 Do(f func()),传入需要执行的函数。
基本用法示例:
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
done := make(chan bool)
// 模拟多个 goroutine 调用
for i := 0; i < 10; i++ {
go func(x int) {
// once.Do() 传入的函数只会执行一次
once.Do(func() {
fmt.Printf("只执行一次: %d\n", x)
})
// 这行每个 goroutine 都会执行
fmt.Printf("goroutine %d 完成\n", x)
done <- true
}(i)
}
// 等待所有 goroutine 完成
for i := 0; i < 10; i++ {
<-done
}
}
常见使用场景:
- 单例模式:
type Singleton struct{}
var (
instance *Singleton
once sync.Once
)
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
- 一次性初始化:
type Config struct {
settings map[string]string
once sync.Once
}
func (c *Config) Load() {
c.once.Do(func() {
// 加载配置的代码
c.settings = make(map[string]string)
c.settings["key"] = "value"
})
}
- 延迟关闭资源:
type Connection struct {
once sync.Once
quit chan struct{}
}
func (c *Connection) Close() {
c.once.Do(func() {
close(c.quit)
})
}
- 错误处理:
type ErrorHandler struct {
once sync.Once
err error
}
func (e *ErrorHandler) HandleError(err error) {
e.once.Do(func() {
e.err = err
// 只处理第一个错误
fmt.Println("处理错误:", err)
})
}
sync.Once 的特点:
- 线程安全:可以在多个 goroutine 中安全使用
- 只执行一次:无论调用多少次,传入的函数只会执行一次
- 所有 goroutine 都会等待第一次执行完成
- 执行失败不会重试:如果函数执行出错,后续调用也不会重新执行
- 不能重置:一旦执行过,就不能重新开始
需要注意的是,sync.Once 的 Do 方法只保证函数执行一次,但不保证是哪个 goroutine 执行的。如果有多个 goroutine 同时调用,只有一个会执行函数,其他的会等待执行完成。