單例模式是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一個類只有一個實例而且該實例易於外界訪問,從而方便對實例個數的控制並節約系統資源。如果希望在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。
1.Go實現非線程安全的單例模式(懶漢 就是很懶的單例 哈哈):
kage singleton type singleton struct { } var instance *singleton func GetInstance() *singleton { if instance == nil { instance = &singleton{} // <--- NOT THREAD SAFE } return instance }
非線程安全的單例模式是大家用得最多的一種。在Github上面的開源項目有很多都使用了非線程安全的。
這種寫法lazy loading很明顯,但是致命的是在多線程不能正常工作。
2.Go實現帶線程鎖的單例模式
varmu Sync.Mutex func GetInstance()*singleton{ mu.Lock() // <--- Unnecessary locking if instance already created defer mu.Unlock() ifinstance==nil{ instance=&singleton{} } returninstance }
這里用了Go的sync/mutex
sync/mutext是Go語言底層基礎對象之一,用於構建多個goroutine間的同步邏輯,因此被大量高層對象所使用。
其工作模型類似於Linux內核的futex對象,具體實現極為簡潔,性能也有保證。
mutex對象僅有兩個數值字段,分為為state(存儲狀態)和sema(用於計算休眠goroutine數量的信號量)。
初始化時填入的0值將mutex設定在未鎖定狀態,同時保證時間開銷最小。
這一特性允許將mutex作為其它對象的子對象使用。
3.帶檢查鎖的的單例模式
func GetInstance()*singleton{ ifinstance==nil{ // <-- Not yet perfect. since it's not fully atomic mu.Lock() defer mu.Unlock() ifinstance==nil{ instance=&singleton{} } } returninstance }
這是一個不錯的方法,但是還並不是很完美。因為編譯器優化沒有檢查實例存儲狀態。如果使用sync/atomic包的話 就可以自動幫我們加載和設置標記。
rt "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 }
個人覺得比較好的在go中使用單例設計的是這種
packagesingleton import( "sync" ) typesingletonstruct{ } varinstance *singleton varonce sync.Once func GetInstance()*singleton{ once.Do(func(){ instance=&singleton{} }) returninstance }
通過使用sync.Once 包可以實現線程安全的單例模式。
如果寫得有什么不對地方歡迎指出。
轉自:https://xiequan.info/go%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/