持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情
前言
程序员必知必会的三个设计模式:单例模式、策略模式、观察者模式。
单例模式:就是在程序运行期间确保某个类只有一个实例。让一个对象不被实例化多个对象,不仅要让其是成为全局变量,还要保证不允许被创建。
只能是一个实例的场景:
- 如生成唯一的系统序列号。整个系统只需要一个实例对象
- 公共访问点只允许一个入口访问,用户调用类的单个实例只允许一个公共访问点,不允许其他的方式访问
单例模式设计演进
要解决只能被实例化一次,处理方式:自己实例化自己
type singleton struct{
}
var instance *singleton
func GetInstance() *singleton {
if instance == nil {
instance = new(singleton)
}
return instance
}
当需要 instance 时都需要先调用 GetInstance 函数,判断 instance 是否存在,不存在才创建。但是对于多线程或者说多协程下,同时调用 GetInstance 函数,就都检测是 nil ,这样就会导致创建 实例并互相覆盖。这并不是线程安全。这也就是所谓饿汉实现方式,没有任何措施控制创建多个。
type singleton struct{
}
var instance *singleton
var lock sync.Mutex
func GetInstance() *singleton {
lock.Lock()
defer lock.Unlock()
if instance == nil {
instance = new(singleton)
}
return instance
}
增加锁控制,解决并发安全问题。但是也存在问题:每次创建实例时都进行加锁解锁,无疑这会增加创建实例的时间。虽然解决了并发时产生的问题,但是如果实例不是 nil 但也是加锁解锁。所以进一步优化的话:
type singleton struct{
}
var instance *singleton
var lock sync.Mutex
func GetInstance() *singleton {
if instance == nil {
lock.Lock()
defer lock.Unlock()
if instance == nil {
instance = new(singleton)
}
}
return instance
}
只有未初始化时才会创建实例,新的问题:每次都会检查两次。
sync.once
Go 并发中 sync.once 作用是使方法只执行一次的对象实现,作用和 init 函数一样。但sync.once 是在代码运行中需要的时候执行,且只执行一次。init 函数是在文件包首次被加载的时候执行,且只执行一次。
// 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 uint32
m Mutex
}
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 0 { // check
// Outlined slow-path to allow inlining of the fast-path.
o.doSlow(f)
}
}
func (o *Once) doSlow(f func()) {
o.m.Lock() // lock
defer o.m.Unlock()
if o.done == 0 { // check
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
- 只有当 调用 Do 方法时,才会执行 f 函数。
- Once 中使用 done 记录函数的执行状态
sync.once 实现单例模式
所以可以使用 sync.once 的 Do 方法让程序在运行过程中之创建一次实例。
type singleton struct {}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = new(singleton)
})
return instance
}
使用 sync.once 不仅优化代码,而且更好的实现了单例模式。