返回
踩坑!初级程序猿须知:Sync.Once的神奇用法
后端
2023-10-11 06:32:38
掌握 sync.Once,轻松驾驭并发编程
前言
在 Go 语言的并发编程中,协调并发任务至关重要。sync.Once 是一个简单但强大的工具,可帮助您确保特定操作只执行一次,即使有多个 goroutine 同时尝试执行。了解并掌握 sync.Once 的用法,将大幅提升您编写健壮、可靠并发程序的能力。
初识 sync.Once
sync.Once 是一种并发类型,它只有一个方法:Do。此方法接收一个函数作为参数,并保证该函数只会被执行一次。也就是说,无论调用 sync.Once.Do 多次,传入的函数都只会执行一次。
使用场景
sync.Once 在以下场景中特别有用:
- 单例模式: 确保只有一个实例被创建。
- 资源初始化: 保证资源只被初始化一次。
- 并发控制: 确保某段代码只被执行一次。
源码剖析
sync.Once 的实现非常简单,它使用了一个互斥锁和一个原子变量:
type Once struct {
m sync.Mutex
done uint32
}
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 0 {
o.m.Lock()
defer o.m.Unlock()
if atomic.LoadUint32(&o.done) == 0 {
f()
atomic.StoreUint32(&o.done, 1)
}
}
}
当调用 sync.Once.Do 时,它首先会检查原子变量是否为 0。如果不是,说明函数已经执行过,它将直接返回,而不会执行函数 f。
如果原子变量为 0,说明函数还没有被执行过,它将获取互斥锁,然后再次检查原子变量的值。如果仍然为 0,则执行函数 f,并将原子变量的值设置为 1,以表示函数已经执行过。
实战应用
以下是 sync.Once 在不同场景中的实际应用:
- 单例模式:
var instance *MyStruct
var once sync.Once
func GetInstance() *MyStruct {
once.Do(func() {
instance = new(MyStruct)
})
return instance
}
- 资源初始化:
var db *sql.DB
var once sync.Once
func GetDB() *sql.DB {
once.Do(func() {
db = sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/database")
})
return db
}
- 并发控制:
var once sync.Once
func MyFunc() {
once.Do(func() {
// do something
})
}
结语
sync.Once 是一个看似简单,但功能强大的并发控制工具。它可以轻松实现单例模式、资源初始化和并发控制等场景。掌握 sync.Once 的使用,将让您在并发编程中游刃有余。
常见问题解答
- sync.Once 和 sync.Mutex 有什么区别?
sync.Once 保证函数只执行一次,而 sync.Mutex 保证一段代码被互斥执行。 - 如何检查 sync.Once 是否已经执行过?
可以使用 atomic.LoadUint32(&o.done) == 1 来检查。 - sync.Once 可以用于哪些并发场景?
单例模式、资源初始化、并发控制。 - sync.Once 的使用是否会影响性能?
正常情况下,sync.Once 的使用不会显著影响性能。 - sync.Once 是否可以用于跨 goroutine 的同步?
可以,但需要将 sync.Once 声明为全局变量。