初入门径
sync.Once提供了保证某个操作只被执行一次的功能,其最常应用于单例模式之下,例如初始化系统配置、保持数据库唯一连接,以及并发访问只需要初始化一次的共享资源。
单例模式有懒汉模式和饿汉模式两种
饿汉模式 顾名思义就是比较饥饿,所以一上来(服务启动时)就初始化。 懒汉模式 顾名思义就是偷懒,在获取实例的时候在进行初始化,但懒汉模式会有并发问题:有可能多个 goruntine 同时获取 对象都是 nil ,然后都开始创建了实例,就不满足单例模式了。解决办法是加锁
sync.Once就是懒汉模式
Go并发编程 — sync.Once
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
fun1 := func() {
fmt.Println("一只老虎")
}
once.Do(fun1)
fun2 := func() {
fmt.Println("两只老虎")
}
once.Do(fun2)
}
输出为:
一只老虎
并发调用 Do()
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var once sync.Once
for i := 0; i < 5; i++ {
go func(i int) {
fun1 := func() {
fmt.Printf("i:=%d\n", i)
}
once.Do(fun1)
}(i)
}
// 为防止主goroutine直接运行完,什么都看不到
time.Sleep(50 * time.Millisecond)
}
在上面这段代码里,开启了5个并发的 goroutine ,不管执行多少次, 始终只打印一次. 至于 i 是多少,就看先执行的是哪个 g 了。
Once 保证只有第一次调用 Do() 方法时, 传递的 f (无参数无返回值的函数) 才会执行,并且之后不管调用的参数是否改变了,也不管f执行时是否发生了panic,之后都不再执行。
这段是官方Demo:
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
onceBody := func() {
fmt.Println("Only once")
}
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
once.Do(onceBody)
done