This is a creation in Article, where the information may have evolved or changed.
Sync.once can control that a function can only be called once and cannot be repeated multiple times.
We can implement a thread-safe singleton pattern with the following code
package singletonimport ( "fmt" "sync")type object struct { name string}var once sync.Oncevar obj *object //单例指针//公开方法 外包调用func Instance() *object { once.Do(getObj) return obj}func getObj() { if obj == nil { obj = new(object) //可以做其他初始化事件 }}//单例测试func (obj *object) Test() { fmt.Println(obj.name)}
What if we're going to do it ourselves?
- Define a status variable to describe whether it has been executed
- Use Sync. Mutex or sync. Atomic implements a thread-safe get status state that determines whether a particular function is executed according to the state
Then look at sync. How the once is actually implemented
// Once is an object that will perform exactly one action.type Once struct { m Mutex done uint32}//使用了双层检查机制 func (o *Once) Do(f func()) { if atomic.LoadUint32(&o.done) == 1 { return } // Slow-path. o.m.Lock() defer o.m.Unlock() //这里需要再次重新判断下,因为 atomic.LoadUint32取出状态值到 o.m.Lock() 之间是有可能存在其它gotoutine改变status的状态值的 if o.done == 0 { f() atomic.StoreUint32(&o.done, 1) }}
Also has the netizen to write the more concise code, did not know why the official did not adopt the following realization way.
type Once struct { done int32}func (o *Once) Do(f func()) { if atomic.LoadInt32(&o.done) == 1 { return } // Slow-path. if atomic.CompareAndSwapInt32(&o.done, 0, 1) { f() }}