sync.Once
是Go语言中的一个同步原语,它用于确保一个函数只被执行一次,即使在多个goroutine中同时调用也可以保证只执行一次。
sync.Once
类型定义如下:
type Once struct {
m Mutex
done uint32
}
sync.Once
结构体中包含一个互斥锁(Mutex)m
和一个done
标志,done
标志用来表示函数是否已经被执行过。done
标志使用了无符号整型uint32
类型,通过原子操作来保证并发安全。
sync.Once
提供了一个方法Do
,用于执行函数,函数类型为func()
。Do
方法的签名如下:
func (o *Once) Do(f func())
Do
方法的逻辑很简单,它首先检查done
标志的值,如果已经被设置为1,则表示函数已经执行过,直接返回;如果done
标志的值为0,则表示函数尚未执行,此时会获取互斥锁m
,再次检查done
标志的值,如果仍然为0,则调用传入的函数f
执行一次,并将done
标志设置为1,释放互斥锁。这样,之后再次调用Do
方法时,由于done
标志已经被设置为1,函数将不会被执行。
使用示例:
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
for i := 0; i < 10; i++ {
go func() {
once.Do(func() {
fmt.Println("This will only be printed once.")
})
}()
}
// 等待goroutine执行完毕
// ...
fmt.Println("Program finished.")
}
在上面的示例中,我们创建了一个sync.Once
实例once
,然后启动了10个goroutine,并且每个goroutine都调用了once.Do
方法来执行函数。由于once.Do
方法保证函数只被执行一次,所以只有第一个调用once.Do
的goroutine会执行函数,其他goroutine调用once.Do
时会直接返回,不再执行函数。
需要注意的是,sync.Once
只能确保在相同的sync.Once
实例上调用Do
方法时函数只执行一次。如果你在不同的sync.Once
实例上调用Do
方法,函数仍然可能被执行多次。