上QQ阅读APP看书,第一时间看更新
8.2.3 sync.Once
编程时可以使用包的init方法初始化一些变量,但是有些变量可能是程序运行很久以后才会用到,甚至有时候自始至终也不会用到某些变量。那么有什么方法可以在第一次用到某变量时再进行初始化呢?
sync.Once就提供了延迟初始化的功能。其结构和方法如下:
type Once struct {//结构体内的变量都是包外不可见的 m Mutex done uint32 } func (o *Once) Do(f func())
结构体内的done用来记录执行次数,用m来保证同一时刻只有一个goroutine在执行Do方法。在使用该结构体的时候先定义Once型变量:
var once Once
然后通过定义的变量向Do方法传入函数,以此作为参数:
once.Do(func() {f()})
即便是多次调用once.Do(f),f也仅仅执行一次。
Once可以解决Mutex和RWMutex不方便解决的问题。如果每次都用Mutex去判断变量是否已经初始化,那么会让所有的goroutine在这一步都是互斥的,而且永远要进行这个操作。RWMutex虽然略好一些,但是会增加系统消耗。Once则可以非常好地解决这个问题,下面来看一个示例:
book/ch08/8.2/once/once.go
1. package main
2.
3. import (
4. "fmt"
5. "sync"
6. )
7.
8. func main() {
9. var wg sync.WaitGroup
10. var once sync.Once
11. for i:=0;i<10;i++{
12. wg.Add(1)
13. go func() {
14. defer wg.Done()
15. once.Do(onlyOnce)
16. }()
17. }
18. wg.Wait()
19. }
20.
21. func onlyOnce() {
22. fmt.Println("only once")
23. }
这是一个简单的示例,本例希望让第一个用到onlyOnce函数的goroutine来调用该函数,而后面的goroutine就不需要再调用了。所以,本例使用了sync.Once的Do方法调用onlyOnce方法。示例代码中虽然启动了10个goroutine,但是执行完成会发现onlyOnce函数仅打印一次。
sync.Once是非常好的延迟初始化的方式,延迟初始化可以提高系统启动的速度,也可以保证只初始化一次。