前言
单例模式是最简单的一种模式。在Go中,单例模式指的是全局只有一个实例,并且它负责创建自己的对象。单例模式有减少内存和系统资源开销、防止多个实例产生冲突等优点。
因为单例模式保证了实例的全局唯一性,并且只被初始化一次,所以比较适合全局共享一个实例,且只需要被初始化一次的场景,例如数据库实例、全局配置、全局任务池等。
单例模式又分为饿汉方式和懒汉方式。饿汉方式是指全局的单例实例在包被加载时创建,而懒汉方式指全局的单例实例在第一次被使用时创建。其中懒汉方式是开源项目中使用最多的方式。
示例代码
Go
懒汉方式的缺点是非并发安全,实际使用中一般加锁,或者使用sync.Once
package singleton
import "sync"
type Singleton interface {
foo()
}
type singleton struct{}
func (s singleton) foo() {}
var (
instance *singleton
once sync.Once
)
func GetInstance() Singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}
单元测试
package singleton
import (
"sync"
"testing")
const parCount = 100
func TestSingleton(t *testing.T) {
ins1 := GetInstance()
ins2 := GetInstance()
if ins1 != ins2 {
t.Fatal("instance is not equal")
}
}
func TestParallelSingleton(t *testing.T) {
start := make(chan struct{})
wg := sync.WaitGroup{}
wg.Add(parCount)
instance := [parCount]Singleton{}
for i := 0; i < parCount; i++ {
go func(index int) {
<-start
instance[index] = GetInstance()
wg.Done()
}(i)
}
close(start)
wg.Wait()
for i := 1; i < parCount; i++ {
if instance[i] != instance[i-1] {
t.Fatal("instance is not equal")
}
}
}
Python
python的包是天然的单例模式,只要放到单独的包中,import时就是引用的单例。
如果要在一个包内使用设计模式,也有以下几种方式。
使用函数装饰器实现单例
def singleton(cls):
_instance = {}
def inner():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return inner
@singleton
class MyCls:
def __init__(self):
pass
if __name__ == "__main__":
a = MyCls()
b = MyCls()
print(id(a) == id(b)) # 输出结果应为 True
使用类装饰器实现单例
class Singleton:
def __init__(self, cls):
self._cls = cls
self._instance = {}
def __call__(self):
if self._cls not in self._instance:
self._instance[self._cls] = self._cls()
return self._instance[self._cls]
@Singleton
class MyCls:
def __init__(self):
pass
if __name__ == "__main__":
a = MyCls()
b = MyCls()
print(id(a) == id(b)) # 输出结果应该是True