golang atomic load 性能_设计模式之Golang单例模式
今天給大家講下什么是單例模式,以及在Go語(yǔ)言中如何用正確的姿勢(shì)實(shí)現(xiàn)它。其實(shí)單例模式是一種在平時(shí)開(kāi)發(fā)中經(jīng)常用到的軟件設(shè)計(jì)模式。在設(shè)計(jì)模式結(jié)構(gòu)中,其核心是只包含一個(gè)被稱(chēng)為單例的特殊類(lèi)。通過(guò)單例模式可以確保系統(tǒng)中一個(gè)類(lèi)只有一個(gè)實(shí)例,且該實(shí)例容易被外界訪問(wèn),從而方便對(duì)實(shí)例數(shù)量的控制并節(jié)約系統(tǒng)資源。
1. 懶漢模式懶漢模式是平時(shí)軟件開(kāi)發(fā)中比較常見(jiàn)的,也是用的最多的一種,該模式的最大缺點(diǎn)就是非線程安全。什么是非線程安全呢,這里就不展開(kāi)講了,后續(xù)會(huì)單獨(dú)拿一期來(lái)講什么是線程安全和非線程安全。
懶漢模式代碼示例如下:
//?定義單例的結(jié)構(gòu)體type?Singleton?struct?{}// 聲明一個(gè)Singleton結(jié)構(gòu)體指針的變量var singleton *Singleton// 獲取單例的函數(shù),返回Singleton結(jié)構(gòu)體指針類(lèi)型func GetSingleton() *Singleton { // 如果為空,則創(chuàng)建單例 if singleton == nil { singleton = &Singleton{} } return singleton}2. 帶普通鎖的單例模式上面講的懶漢模式,其實(shí)是沒(méi)有帶鎖的,也就是并發(fā)處理時(shí)會(huì)發(fā)生競(jìng)爭(zhēng),從而會(huì)導(dǎo)致程序出錯(cuò)退出。
普通鎖可以解決并發(fā)處理帶來(lái)的問(wèn)題,這里說(shuō)的普通鎖其實(shí)是使用了Go的sync.Mutex,其缺點(diǎn)就是要人工維護(hù)鎖的添加和釋放,在有些時(shí)候?qū)︽i的使用不當(dāng)?shù)脑?#xff0c;反而會(huì)帶來(lái)不必要的性能消耗,事倍功半。?其工作原理類(lèi)似于Linux內(nèi)核的futex對(duì)象,具體實(shí)現(xiàn)原理這里就不詳細(xì)展開(kāi)講來(lái),sync.Mutex的原理后面也會(huì)單獨(dú)用一期來(lái)講。
帶普通鎖的單例模式示例代碼如下:
// 定義單例的結(jié)構(gòu)體type Singleton2 struct {}// 聲明一個(gè)Singleton結(jié)構(gòu)體指針的變量var instance *Singleton2// 聲明普通鎖變量,muvar mu sync.Mutex// 獲取帶鎖的單例函數(shù)func GetInstance() *Singleton2 { // 加鎖 mu.Lock() // 函數(shù)退出前解鎖 defer mu.Unlock() // 如果為空,則創(chuàng)建單例 if instance == nil { instance = &Singleton2{} } return instance}3. 比較優(yōu)雅的單例模式以上兩鐘單例模式都有缺點(diǎn),不是那么完美,而Golang本身sync.Once庫(kù)里就已經(jīng)很好的實(shí)現(xiàn)了單例模式。
示例代碼如下:
// 創(chuàng)建一個(gè)結(jié)構(gòu)體type?Manager?struct?{}// 聲明兩個(gè)全局變量,一個(gè)是Manager結(jié)構(gòu)體指針,一個(gè)是用于單例等nocevar m *Managervar once sync.Once// 創(chuàng)建Manager單例函數(shù)func GetManage() *Manager {??//?once.Do已經(jīng)優(yōu)雅的封裝好了部分加鎖的代碼 once.Do(func() { m = &Manager{} }) return m}簡(jiǎn)單來(lái)說(shuō),sync.Once表示只執(zhí)行一次函數(shù),要做到這點(diǎn),需要有兩個(gè)條件:
計(jì)數(shù)器,統(tǒng)計(jì)函數(shù)執(zhí)行的次數(shù)
線程安全,保證在多個(gè)goroutine(并發(fā))情況下,函數(shù)仍然只執(zhí)行一次,這里面其實(shí)也涉及到鎖
其中sync.Once實(shí)現(xiàn)的核心函數(shù)就是Do(),看一下Do函數(shù)的源碼,如下:
// Once源碼的數(shù)據(jù)結(jié)構(gòu)type Once struct { m Mutex done uint32}// Do函數(shù)實(shí)現(xiàn)func (o *Once) Do(f func()) { if atomic.LoadUint32(&o.done) == 1 { return???} o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() }}根據(jù)以上的源碼可以看到是定義了Once結(jié)構(gòu)體,其中的done成員就是用于統(tǒng)計(jì)函數(shù)執(zhí)行的次數(shù)的,m成員是鎖,保障線程安全的。
Do方法也比較簡(jiǎn)單:
首先原子操作load函數(shù)執(zhí)行次數(shù),如果已經(jīng)執(zhí)行過(guò)了,就return
然后lock加鎖
執(zhí)行函數(shù),原子操作store函數(shù)執(zhí)行次數(shù),賦值為1
解鎖unlock
如果換做是我們來(lái)寫(xiě)的話,一般就會(huì)先直接加鎖,然后再比較函數(shù)執(zhí)行的次數(shù),而這里的源碼用了原子操作,這樣可以提高性能。總的來(lái)說(shuō),一些標(biāo)志位可以通過(guò)原子操作來(lái)表示,從而避免加鎖,提高性能。
以上完整示例代碼已歸檔到我的github,如有需要?dú)g迎下載學(xué)習(xí)交流:https://github.com/Scoefield/gokeyboardman/tree/main/singletonmode
本期設(shè)計(jì)模式之Golang單例模式的介紹就到這里啦,感謝您的閱讀,如有疑問(wèn)或意見(jiàn)請(qǐng)及時(shí)反饋給我們。
上一篇文章:
Golang中的“包”
總結(jié)
以上是生活随笔為你收集整理的golang atomic load 性能_设计模式之Golang单例模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C++编译过程
- 下一篇: 利用计算机录音模拟声波,2016年计算机