雖然我個人覺得這個pattern應該是史上被濫用最多的pattern,濫用到快要變成anti-pattern了,不過仍然是佔有一席之地的。聊一下在go裡面singleton通常是怎麼實作的。

Go本身並沒有提供Singleton,而我其實個人也不是很愛Singleton Pattern。不過有些設計缺他不可,比如說某些設計下System Context就必須要是一個Singleton,所以還是得看看要怎麼在Go裡面實作出來。

最直覺的做法

type singleton struct {
}

var instance *singleton

func GetInstance() *singleton {
	if instance == nil {
		instance = &singleton{}
	}
	return instance
}

這做法很直覺,大多數的情況來講應該也夠用了。但是這個最明顯的問題就是instance = &singleton{}這段顯然在multi thread會有點問題…

那….第二直覺的做法

var mu Sync.Mutex

func GetInstance() *singleton {
    mu.Lock()                        
    defer mu.Unlock()

    if instance == nil {
        instance = &singleton{}
    }
    return instance
}

多虧go有defer這個關鍵字,整個mutex的做法變得很輕鬆。但是…為什麼要鎖那麼大的範圍啊? XD 整個GetInstance的效能都被這個大範圍地圖鎖給搞爛了…

好像也頗符合直覺的做法?

var mu Sync.Mutex

func GetInstance() *singleton {

    if instance == nil {
        mu.Lock()
        defer mu.Unlock()
        if instance == nil {
                instance = &singleton{}
        }
    }
    return instance
}

有完沒完(翻桌),這做法不能說錯,不過要把code寫成這樣還真的需要滿高恥力的,幸好Go有發現這點,為了讓我們寫Singleton的時候不至於羞愧而亡,他們有提供了once

使用once?

目前來講比較常見的合理做法是使用go內建的once,這可以保證裡面的code只會被跑一次,這大概長得像這樣

var instance *singleton
var once sync.Once

func GetInstance() *singleton {
    once.Do(func() {
        instance = &singleton{}
    })
    return instance
}

其實可以參考一下once原始碼,寫的滿漂亮的。簡單的說,就是在裡面放一個atomic int來當作flag,當int > 0就會直接跳出,否則就會執行Do並且把atomic int +1。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *