// 多次调用仅执行一次指定的函数 ffunc (o *Once) Do(f func())
例的fooOnce函数只执行一次打印。
func fooOnce(){fmt.Println("只会执行一次")}func main() {var once sync.Oncedone := make(chan int)for i := 0; i < 10; i++ {go func(index int) {once.Do(fooOnce)done <- index}(i)}for i := 0; i < 10; i++ {fmt.Println("接手到的数据",<-done)}}
单例模式
package mainimport ("fmt""sync")type Singleton struct {}var singleInstance *Singletonvar once sync.Oncefunc GetSingletonObj() *Singleton {once.Do(func() {fmt.Println("Create object")singleInstance = new(Singleton)})return singleInstance}func main() {var wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go func() {obj := GetSingletonObj()fmt.Printf("%p\n", obj)wg.Done()}()}wg.Wait()}
源码
package syncimport ("sync/atomic")// Once is an object that will perform exactly one action.type Once struct {// done indicates whether the action has been performed.// It is first in the struct because it is used in the hot path.// The hot path is inlined at every call site.// Placing done first allows more compact instructions on some architectures (amd64/x86),// and fewer instructions (to calculate offset) on other architectures.done uint32m Mutex}// Do calls the function f if and only if Do is being called for the// first time for this instance of Once. In other words, given// var once Once// if once.Do(f) is called multiple times, only the first call will invoke f,// even if f has a different value in each invocation. A new instance of// Once is required for each function to execute.//// Do is intended for initialization that must be run exactly once. Since f// is niladic, it may be necessary to use a function literal to capture the// arguments to a function to be invoked by Do:// config.once.Do(func() { config.init(filename) })//// Because no call to Do returns until the one call to f returns, if f causes// Do to be called, it will deadlock.//// If f panics, Do considers it to have returned; future calls of Do return// without calling f.//func (o *Once) Do(f func()) {// Note: Here is an incorrect implementation of Do://// if atomic.CompareAndSwapUint32(&o.done, 0, 1) {// f()// }//// Do guarantees that when it returns, f has finished.// This implementation would not implement that guarantee:// given two simultaneous calls, the winner of the cas would// call f, and the second would return immediately, without// waiting for the first's call to f to complete.// This is why the slow path falls back to a mutex, and why// the atomic.StoreUint32 must be delayed until after f returns.if atomic.LoadUint32(&o.done) == 0 {// Outlined slow-path to allow inlining of the fast-path.o.doSlow(f)}}func (o *Once) doSlow(f func()) {o.m.Lock()defer o.m.Unlock()if o.done == 0 {defer atomic.StoreUint32(&o.done, 1)f()}}
