Go單例模式與Once源碼實現(xiàn)
單例實現(xiàn)
type singleton struct{} var ( instance *singleton initialized uint32 mu sync.Mutex ) func Instance() *singleton { if atomic.LoadUint32(&initialized) == 1 { return instance } mu.Lock() defer mu.Unlock() if instance == nil { defer atomic.StoreUint32(&initialized, 1) instance = &singleton{} } return instance }
其中通用的代碼提取出來,就成了標(biāo)準(zhǔn)庫中sync.Once
的實現(xiàn):
type Once struct { done uint32 m sync.Mutex } func (o *Once) Do(f func()) { if atomic.LoadUint32(&o.done) == 0 { o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() } } }
于是,使用sync.Once
重新實現(xiàn)單例模式
var ( instance2 *singleton once sync.Once ) func Instance2() *singleton { once.Do(func() { instance2 = &singleton{} }) return instance2 }
sync.Once源碼分析
1. lock并不會同步值
在lock和unlock之間修改值,并不會保證對其他協(xié)程是可見的,除非使用相同的Mutex加鎖,想要同步值必須使用atomic;
lock可以通過串行化,使得兩個協(xié)程的操作存在happen-before
關(guān)系,從而是的操作可見
happen-before
原則定義如下:
如果一個操作happens-before(之前發(fā)生)另一個操作,那么第一個操作的執(zhí)行結(jié)果將對第二個操作可見,而且第一個操作的執(zhí)行順序排在第二個操作之前。
兩個操作之間存在happens-before關(guān)系,并不意味著一定要按照happens-before原則制定的順序來執(zhí)行。如果重排序之后的執(zhí)行結(jié)果與按照happens-before關(guān)系來執(zhí)行的結(jié)果一致,那么這種重排序并不非法。
2. Do執(zhí)行一次
當(dāng)?shù)谝淮螆?zhí)行完Do
之后,done
設(shè)置成1,后面執(zhí)行Do
會直接跳過
3. Once執(zhí)行Do后不準(zhǔn)copy
A Once must not be copied after first use.
sync.Once
執(zhí)行完Do
后done
已經(jīng)設(shè)置成1了,copy出來的once執(zhí)行Do
會直接跳過
4. Do并發(fā)時阻塞
當(dāng)兩個或者多個協(xié)程同時調(diào)用Do
時,先調(diào)用的協(xié)程執(zhí)行,后面的協(xié)程會阻塞;
解釋:以單例使用once的實現(xiàn)說明,兩個協(xié)程同時調(diào)用Instance2()
,先調(diào)用的協(xié)程執(zhí)行創(chuàng)建并拿到返回值,后調(diào)用的阻塞,
? 等到先調(diào)用的完成后再拿到返回值;
意義:這樣的好處是防止后調(diào)用的協(xié)程拿到的是nil
源碼說明:上面第二段代碼13行使用defer
,要等f()
結(jié)束才會把done
設(shè)置成1;其他協(xié)程并發(fā)調(diào)用Do
時,done==0
,
? 然后請求m.Lock()
形成阻塞
5. Do遞歸死鎖
如果Do
中的方法調(diào)用當(dāng)前once的Do
會造成死鎖,原因參考上面一點(sync.Mutex.Lock()
時不可重入鎖)
參考
- 《Go語言高級編程》
- Go1.16源碼
相關(guān)文章
go從指定的URL下載圖片并保存到本地的代碼實現(xiàn)
這段代碼定義了一個名為 downloadImage 的函數(shù),其目的是從指定的URL下載圖片并保存到本地文件系統(tǒng),本文是對代碼功能的詳細(xì)描述,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-08-08golang?chan傳遞數(shù)據(jù)的性能開銷詳解
這篇文章主要為大家詳細(xì)介紹了Golang中chan在接收和發(fā)送數(shù)據(jù)時因為“復(fù)制”而產(chǎn)生的開銷,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2024-01-01利用Go語言快速實現(xiàn)一個極簡任務(wù)調(diào)度系統(tǒng)
任務(wù)調(diào)度(Task Scheduling)是很多軟件系統(tǒng)中的重要組成部分,字面上的意思是按照一定要求分配運行一些通常時間較長的腳本或程序。本文將利用Go語言快速實現(xiàn)一個極簡任務(wù)調(diào)度系統(tǒng),感興趣的可以了解一下2022-10-10golang?gorm的Callbacks事務(wù)回滾對象操作示例
這篇文章主要為大家介紹了golang?gorm的Callbacks事務(wù)回滾對象操作示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04