单例模式

go最大的优势之一在于在语言层面通过goroutine实现了并发编程,提高了执行效率;但是并发随之带来的事就如何保证线程安全。通过我们会使用单例模式来保证线程安全,即在程序中我们只需要某个“类”实例化一次即可,保证一个“类“仅有一个实例,并提供一个获取实例的方法。

小刀初试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Instance ...
type Instance struct {
Time int64
}

var instance *Instance
func getInstance() *Instance {
if instance == nil {
instance = &Instance{
Time: time.Now().Unix(),
}
}

return instance
}

但是在高并发情况下无法保证其线程的安全性。

简单的锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Instance ...
type Instance struct {
Time int64
}

var instance *Instance
var mu sync.Mutex

func getInstance() *Instance {
mu.Lock()
defer mu.Unlock()
if instance == nil {
instance = &Instance{
Time: time.Now().Unix(),
}
}

return instance
}

此时是可以解决并发线程安全的问题,但是每次获取实例的时候都需要经过加锁解锁的过程,造成性能不必要的损耗。

再进一步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Instance ...
type Instance struct {
Time int64
}

var instance *Instance
var mu sync.Mutex

func getInstance() *Instance {
if instance == nil {
mu.Lock()
defer mu.Unlock()

instance = &Instance{
Time: time.Now().Unix(),
}
}

return instance
}

是有在创建的时候才需要添加锁,避免不必要的锁。

首选Sync.Atomic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Instance ...
type Instance struct {
Time int64
}

var instance *Instance
var once sync.Once

func getInstance() *Instance {
once.Do(func() {
instance = &Instance{
Time: time.Now().Unix(),
}
})

return instance
}

题外话:Sync.Once原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 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()
}
}

使用互斥锁实现原子操作