# golang单例模式

# 常见的错误

# 多线程不安全

错误示范

  • 创建该singleton类型的实例存在相互覆盖的可能
type singleton struct {}
var instance *singleton
func GetInstance() *singleton {
	if instance == nil {
		instance = &singleton{}   // 不是并发安全的
	}
	return instance
}
1
2
3
4
5
6
7
8

# 正确但不推荐的单例模式

# 使用激进的锁

  • 在高度并发环境,同时只有一个协程能获得这个单例对象,存在性能瓶颈

激进的锁让函数并行变成串行,可能存在问题件

var mu Sync.Mutex
func GetInstance() *singleton {
    mu.Lock()                    // 如果实例存在没有必要加锁
    defer mu.Unlock()

    if instance == nil {
        instance = &singleton{}
    }
    return instance
}
1
2
3
4
5
6
7
8
9
10

# Check-Lock-Check 模式

  • 最小锁定的思想
func GetInstance() *singleton {
    // 不太完美 因为这里不是完全原子的
    // 高并发依旧存在问题
    if instance == nil {     
        mu.Lock()
        defer mu.Unlock()

        if instance == nil {
            instance = &singleton{}
        }
    }
    return instance
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 使用原子包 sync/atomic(比较繁琐了)
import "sync"
import "sync/atomic"
var initialized uint32
... // 此处省略
func GetInstance() *singleton {
    if atomic.LoadUInt32(&initialized) == 1 {  // 原子操作 
		    return instance
	  }
    mu.Lock()
    defer mu.Unlock()
    if initialized == 0 {
         instance = &singleton{}
         atomic.StoreUint32(&initialized, 1)
    }
    return instance
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 单例模式最佳实践

# 先说方法, 使用官方封装好的Once包实现原子操作

  • 简单看下官方Once 实现方法
// 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()
	}
}
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

# 最佳实践 💯

import sync
// 单利的 业务结构体
type single struct {}
// 单利的对象
var s *single
// 定义一个 Once 对象
var once sync.Once
func GetSingle() *single {
    // 使用once实现单利模式
    once.Do(func() {
        s = &single{}
    })
    return s
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# tip 学习代码的最佳实践非常有用

  • 多学习最佳实践