sync.Once基本原理
- 数据结构:
sync.Once
结构体只有一个字段 done uint32
,以及一个未导出的互斥锁 m sync.Mutex
。
type Once struct {
done uint32
m sync.Mutex
}
- Do方法:
Once.Do(f func())
方法用来确保函数 f
只被执行一次。
- 首先检查
done
字段,如果 done
已经被设置(值不为0),则直接返回,说明初始化操作已经完成,无需再次执行 f
。
- 如果
done
为0,获取互斥锁 m
,再次检查 done
(双重检查锁定),因为在获取锁之前,其他协程可能已经完成了初始化。
- 若
done
仍然为0,则执行函数 f
,执行完后将 done
设置为1,表示初始化已完成,最后释放互斥锁。
在插件加载场景中的应用
- 场景描述:在插件加载时,通常有一些初始化操作,如初始化数据库连接、配置加载等,这些操作只需要执行一次。
- 保证只执行一次初始化:
- 可以在插件加载的代码中定义一个
sync.Once
实例,例如:
var once sync.Once
func loadPlugin() {
// 初始化操作,例如连接数据库、加载配置文件等
println("Plugin initialized")
}
func GetPlugin() {
once.Do(loadPlugin)
// 插件使用相关代码
}
- 不同的协程在调用 `GetPlugin` 时,`loadPlugin` 函数只会被执行一次。因为 `sync.Once` 的机制保证了无论有多少个协程并发调用 `once.Do(loadPlugin)`,`loadPlugin` 函数只会在第一次调用时执行,后续调用直接返回,从而确保插件初始化操作只执行一次。