单例模式是常用的模式之一,一般介绍的单例模式有 饿汉式
和 懒汉式
等,不管那种模式最终目的只有一个,就是只实例化一次,仅允许一个实例存在。
GO语言实现单例模式相对简单,这里考虑到并发,用到了sync.Mutex 和结构体sync.Once。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package main
import ( "fmt" "sync" )
var ( lock *sync.Mutex = &sync.Mutex{} instance *Singleton ) type Singleton struct { }
func GetInstance() *Singleton { if instance == nil { lock.Lock() defer lock.Unlock() if instance == nil { instance = &Singleton{} fmt.Println("instance...") } } return instance }
func main() { var s *Singleton s = GetInstance() s = GetInstance() fmt.Println(s) }
|
执行结果:
instance…
&{}
通过结果可以看到只输出了一个instance…。
上面的实现方式还可以通过结构体sync.Once更优雅的实现。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package main
import ( "fmt" "sync" )
var ( once sync.Once instance *Singleton )
type Singleton struct { }
func GetInstance() *Singleton { once.Do(func() { instance = &Singleton{} fmt.Println("instance...") }) return instance }
func main() { var s *Singleton s = GetInstance() s = GetInstance() fmt.Println(s) }
|
输出结果:
instance…
&{}
通过sync.Once的源代码查看它是如何运行的
1 2 3 4 5 6 7 8 9 10 11 12
| func (o *Once) Do(f func()) { if atomic.LoadUint32(&o.done) == 1 { return } // Slow-path. o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() } }
|
sync.Once.Do(f func())使用加锁原子操作(代码包sync/atomic)来保证函数 f 只执行一次。