单例模式的基本定义
单例模式(singleton pattern)是一种设计模式,旨在保证一个类只有一个实例,并且提供全局访问点。单例模式通常用于需要限制某个对象的实例数量为一个的场景,比如数据库连接池、日志管理器、配置管理器等。
go语言单例模式的实现
1. 线程安全的懒汉式单例
懒汉式的实现会延迟实例的创建,直到第一次调用时才会实例化对象。为了保证并发情况下的安全性,我们需要使用 sync.once 来确保实例只会创建一次。
package main
import (
"fmt"
"sync"
)
var wg sync.waitgroup
// singleton 类型
type singleton struct {
}
var instance *singleton
var once sync.once
// getinstance 提供全局唯一的实例
func getinstance() *singleton {
once.do(func() {
instance = &singleton{}
})
return instance
}
func main() {
// 获取单例实例
for i := 0; i < 10; i++ {
wg.add(1)
go func(index int) {
defer wg.done()
s1 := getinstance()
fmt.printf("index %d, memery address: %p\n", index, s1)
}(i)
}
wg.wait()
}
结果
index 0, memery address: 0x56c480
index 5, memery address: 0x56c480
index 4, memery address: 0x56c480
index 2, memery address: 0x56c480
index 7, memery address: 0x56c480
index 9, memery address: 0x56c480
index 6, memery address: 0x56c480
index 8, memery address: 0x56c480
index 3, memery address: 0x56c480
index 1, memery address: 0x56c480
解析:
sync.once:go标准库提供的一个同步原语,确保某个函数只会被调用一次。它在并发情况下保证了线程安全。once.do:此方法确保传入的函数只执行一次,适用于懒加载单例实例。
2. 双重检查锁定(dcl)
双重检查锁定是一种优化方式,它通过在两次检查实例时,减少了加锁的开销,提高了性能。
package main
import (
"fmt"
"sync"
)
var wg sync.waitgroup
// singleton 类型
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 = &singleton{}
}
}
return instance
}
func main() {
// 获取单例实例
for i := 0; i < 10; i++ {
wg.add(1)
go func(index int) {
defer wg.done()
s1 := getinstance()
fmt.printf("index %d, memery address: %p\n", index, s1)
}(i)
}
wg.wait()
}
解析:
- 双重检查:第一次检查实例是否为
nil,如果是,则加锁。然后再次检查实例是否为nil,如果是则创建实例。 - 锁的优化:只有在实例尚未创建时才会加锁,避免了每次获取实例时都需要加锁的性能损耗。
3. 原子操作法
go 语言的 sync/atomic 包提供了原子操作,我们可以利用它来确保单例的线程安全。
package main
import (
"fmt"
"sync"
"sync/atomic"
"unsafe"
)
var wg sync.waitgroup
type singleton struct {
}
var instance unsafe.pointer
func getinstance() *singleton {
// 使用原子操作获取实例
if atomic.loadpointer(&instance) == nil {
newinstance := &singleton{}
atomic.storepointer(&instance, unsafe.pointer(newinstance))
}
return (*singleton)(atomic.loadpointer(&instance))
}
func main() {
// 获取单例实例
for i := 0; i < 10; i++ {
wg.add(1)
go func(index int) {
defer wg.done()
s1 := getinstance()
fmt.printf("index %d, memery address: %p\n", index, s1)
}(i)
}
wg.wait()
}
解析:
unsafe.pointer:在go中,unsafe.pointer可以用来绕过类型系统,直接处理内存地址。通过原子操作确保实例赋值的安全性。atomic.loadpointer和atomic.storepointer:原子加载和存储指针,确保操作的线程安全性。
总结
在go语言中实现单例模式有多种方式,最常见的是使用 sync.once、双重检查锁定(dcl)和原子操作法。每种方法有其优缺点,选择合适的方式可以帮助你在保证线程安全的前提下优化性能。
到此这篇关于go语言优雅实现单例模式的多种方式的文章就介绍到这了,更多相关go实现单例模式内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论