设计模式不分语言,是一种思维层面的体现,但是不能在不同语言中使用同一套实现(每种语言有不同的特性),比如go,本身是没有继承一说,但是通过结构体的组合来实现语义上的继承。而多态也是通过接口的方式来实现的。
下方的图来自于大佬博客,贴在这里方便查看!!!
设计原则
设计模式
创建型模式
单例模式
分为懒汉式和饿汉式。
饿汉式
//饿汉式
type singleton struct {
}
var instance *singleton
//在go文件的加载顺序中,先加载init,后加载main
func init() {
instance = &singleton{}
}
//获取实例
func GetInstance() *singleton {
return instance
}
懒汉式
形式一: 使用once包保证只会调用一次
import "sync"
var (
lazySingleton *singleton
once = &sync.Once{}
)
//懒汉式获取实例,双重检查
func GetLazyInstance() *singleton {
once.Do(func() { //第二次检查,因为once只会调用一次
lazySingleton = &singleton{}
})
return lazySingleton
}
形式二: 内部实现double check ,和once源码写的一样
type lock struct {
mutex sync.Mutex
done uint32
}
var (
l = &lock{}
lazySingleton2 *singleton
)
func GetLazyInstance2() *singleton {
if atomic.LoadUint32(&l.done) == 0 {
l.mutex.Lock()
//这里有个小技巧,使用defer栈来做资源释放
defer l.mutex.Unlock()
if l.done == 0 {
defer atomic.StoreUint32(&l.done, 1)
lazySingleton2 = &singleton{}
}
}
return lazySingleton2
}
除此之外
懒汉式中使用了double check,其实现逻辑是在once中体现。
type Once struct {
done uint32 //用来配合原子更新
m Mutex //锁
}
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 0 { //第一次check
//直接调方法,进入加锁的一步
o.doSlow(f)
}
}
func (o *Once) doSlow(f func()) {
o.m.Lock() // 加锁
defer o.m.Unlock() //return后解锁
if o.done == 0 { //第二次check
defer atomic.StoreUint32(&o.done, 1) //return后原子更新
f() //执行你想要的逻辑
}
}
疑问:
在once的双重检查中,修改once.done的值,源代码使用的是atomic进行赋值,但是我的理解是在临界区之中直接使用once.done = 1即可。
之后我在本地复刻了once,并且只修改了once.done这一行代码。同时开很多协程去执行,发现依然能履行once本身的意义。
那么问题来了,源代码里的原子赋值是否有意义呢?~跪求大佬告知