2.5 Once
在并发编程中,有时我们需要确保某些初始化操作只执行一次,无论有多少个 goroutine 在同时运行。这类场景在实际开发中非常常见,例如单例模式的初始化、配置文件的加载等。Go 语言中的 sync.Once 提供了一种简洁而高效的方法来实现这一需求。下面我们详细介绍 Once 的概念、使用方法及示例。
2.5.1 什么是Once
Once 是 Go 标准库 sync 包中的一种同步原语,用于确保某些操作只执行一次。无论有多少个 goroutine 同时调用 Do 方法,Once 保证传入的函数只会被执行一次。
Once 主要有一个方法:
Do:接收一个无参数、无返回值的函数,确保该函数只执行一次。如果有多个 goroutine 同时调用Do,只有一个 goroutine 会执行传入的函数,其他 goroutine 会在该函数执行完成后立即返回。
2.5.2 Once的使用方法
使用 Once 非常简单。首先,创建一个 Once 实例。然后,将需要只执行一次的初始化操作封装在一个函数中,并传递给 Do 方法。以下是基本使用示例:
package main
import (
"fmt"
"sync"
)
var once sync.Once
func initialize() {
fmt.Println("Initialization function executed")
}
func main() {
for i := 0; i < 10; i++ {
go func(i int) {
once.Do(initialize)
fmt.Printf("Goroutine %d finished\n", i)
}(i)
}
}
在这个例子中,无论有多少个 goroutine 调用 once.Do(initialize),initialize 函数都只会被执行一次。
2.5.3 Once的应用场景
Once 适用于以下场景:
- 单例模式:确保单例对象的创建操作只执行一次。
- 全局资源初始化:例如全局配置文件的加载、数据库连接的初始化等。
- 延迟初始化:确保某些资源在需要时才被初始化,并且只初始化一次。
2.5.4 示例代码
以下是一个使用 Once 实现单例模式的示例:
package main
import (
"fmt"
"sync"
)
type Singleton struct {
value int
}
var (
instance *Singleton
once sync.Once
)
func getInstance() *Singleton {
once.Do(func() {
instance = &Singleton{value: 42}
fmt.Println("Singleton instance created")
})
return instance
}
func main() {
for i := 0; i < 10; i++ {
go func(i int) {
s := getInstance()
fmt.Printf("Goroutine %d: %d\n", i, s.value)
}(i)
}
// Wait for all goroutines to finish
var wg sync.WaitGroup
wg.Add(10)
go func() {
wg.Wait()
}()
}
在这个示例中,无论有多少个 goroutine 调用 getInstance,Singleton 实例都只会被创建一次。
结论
Once 提供了一种简单而高效的方法,确保某些初始化操作只执行一次,从而避免了复杂的同步操作和潜在的竞态条件。在实际应用中,合理使用 Once 可以简化并发程序的设计,确保初始化操作的安全性和正确性。在接下来的章节中,我们将继续探讨其他同步原语和并发编程技巧,帮助您更好地掌握 Go 的并发编程。