介绍

单例模式同时解决了两个问题:

  • 保证一个类只有一个实例,例如控制某些共享资源(如数据库或文件)的访问权限
  • 为该实例提供一个全局访问节点

Go中单例模式有两种实现,一种是饿汉式,一种是懒汉式饿汉式简单,可以将问题及早暴露出来,懒汉式虽然支持延迟加载,但是也将可能的问题延迟到了第一次调用的时候,同时为了实现并发安全,也不得不加锁。

饿汉式

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Package singleton
package singleton

// singleton 饿汉式
var singleton *File

type File struct{}

func init() {
	singleton = &File{}
}

func GetInstance() *File {
	return singleton
}

单元测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package tests

import (
	"testing"

	"design-pattern/singleton"
	"github.com/stretchr/testify/assert"
)

// TestSingleton 测试单例
func TestInstance(t *testing.T) {
	assert.Equal(t, singleton.GetInstance(), singleton.GetInstance())
}

// BenchmarkSingleton 测试并发访问单例
func BenchmarkInstance(b *testing.B) {
	b.RunParallel(func(p *testing.PB) {
		for p.Next() {
			assert.Equal(b, singleton.GetInstance(), singleton.GetInstance())
		}
	})
}

懒汉式

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Package lazysingleton 懒汉式单例
package lazysingleton

import "sync"

var (
	lazySingleton *File
	once          sync.Once
)

type File struct{}

func GetLazyInstance() *File {
	if lazySingleton == nil {
		// 使用sync.Once来确保单例在并发时只被初始化一次
		once.Do(func() {
			lazySingleton = &File{}
		})
	}
	return lazySingleton
}

单元测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package tests

import (
	"testing"

	"design-pattern/lazysingleton"
	"github.com/stretchr/testify/assert"
)

// TestLazySingleton 测试懒汉式单例
func TestLazyInstance(t *testing.T) {
	assert.Equal(t, lazysingleton.GetLazyInstance(), lazysingleton.GetLazyInstance())
}

// BenchmarkGetLazyInstance 测试懒汉式单例
func BenchmarkGetLazyInstance(b *testing.B) {
	b.RunParallel(func(p *testing.PB) {
		for p.Next() {
			assert.Equal(b, lazysingleton.GetLazyInstance(), lazysingleton.GetLazyInstance())
		}
	})
}

测试结果

1
2
3
4
5
6
7
8
❯ go test -bench .
goos: darwin
goarch: arm64
pkg: design-pattern/tests
BenchmarkGetLazyInstance-8   	 3113553	       380.8 ns/op
BenchmarkInstance-8          	 3156261	       378.5 ns/op
PASS
ok  	design-pattern/tests	3.798s

根据bench结果可以发现加锁(sync.Once内部使用的atomic来进行处理)还是会对性能造成影响的,不过也在可接受范围内。