sync.Once
是 sync
包下的一个组件,它保证某些操作只会被执行一次,无论有多少个 goroutine 调用它。常见的使用场景包括初始化操作,确保某段代码(如初始化函数)只执行一次。
关键思想:通过原子操作和内存屏障的配合,确保在多线程环境下某些操作只会执行一次。
sync.Once
使用起来非常简单,只需要调用 Do
方法,传入一个无参无返回值的函数即可。
以下为基本使用示例:
package main
import (
"fmt"
"sync"
)
var once sync.Once
func initialize() {
fmt.Println("初始化操作")
}
func main() {
for i := 0; i < 10; i++ {
go func() {
once.Do(initialize)
}()
}
}
运行结果:
初始化操作
即使有多个 goroutine 调用 once.Do(initialize)
,initialize
函数也只会被执行一次。
sync.Once
的结构体定义如下:
type Once struct {
m Mutex
done uint32
}
m
:互斥锁,确保同一时间只有一个 goroutine 执行 Do
方法。done
:标志变量,记录操作是否已经执行。Do
方法是 sync.Once
的核心,其实现如下:
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 1 {
return
}
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
主要逻辑包括:
LoadUint32
读取 done
变量,如果为 1,表示操作已经执行,直接返回。done
变量,确保在加锁前没有其他 goroutine 执行。f
,并在执行完成后将 done
变量设置为 1。sync.Once
使用了 atomic.LoadUint32
和 atomic.StoreUint32
来读取和设置 done
变量。原子操作可以确保在多线程环境下的安全性和高效性。
import "sync/atomic"
atomic.LoadUint32(&o.done)
atomic.StoreUint32(&o.done, 1)
内存屏障用于防止编译器和 CPU 进行指令重排,确保 done
变量的读写操作的顺序性。在 sync.Once
中,原子操作隐含了内存屏障的功能。
sync.Once
使用了互斥锁(Mutex
)来确保某些操作在多 goroutine 环境下的安全性。加锁可以确保临界区的代码段在同一时间只能被一个 goroutine 执行。
o.m.Lock()
defer o.m.Unlock()
在高并发的应用中,可以使用 sync.Once
确保数据库连接只被初始化一次。
package main
import (
"database/sql"
"fmt"
"sync"
)
var once sync.Once
var db *sql.DB
func initializeDB() {
var err error
db, err = sql.Open("mysql", "user:password@/dbname")
if err != nil {
panic(err)
}
}
func getDB() *sql.DB {
once.Do(initializeDB)
return db
}
func main() {
for i := 0; i < 10; i++ {
go func() {
db := getDB()
fmt.Println(db)
}()
}
}
在实现单例模式时,可以使用 sync.Once
确保对象只被初始化一次。
package main
import (
"fmt"
"sync"
)
type Singleton struct{}
var (
instance *Singleton
once sync.Once
)
func getInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
func main() {
for i := 0; i < 10; i++ {
go func() {
s := getInstance()
fmt.Println(s)
}()
}
}
sync.Once
使用起来非常简单,只需要调用 Do
方法,传入一个无参无返回值的函数即可。Do
方法传入的函数不能有返回值,因为 sync.Once
不会处理函数的返回值。sync.Once
适用于初始化操作、单例模式等需要确保只执行一次的场景。
如果您喜欢我的文章,请点击下面按钮随意打赏,您的支持是我最大的动力。
最新评论