欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

go單例實(shí)現(xiàn)雙重檢測(cè)是否安全的示例代碼

 更新時(shí)間:2022年03月09日 11:47:58   作者:NO0b  
這篇文章主要介紹了go單例實(shí)現(xiàn)雙重檢測(cè)是否安全,本文給大家分享雙重檢驗(yàn)示例代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

今天看到項(xiàng)目中的kafka客戶端包裝結(jié)構(gòu)體的獲取是單例模式<br>單例的實(shí)現(xiàn)是老生常談的問(wèn)題了,懶漢餓漢線程安全,因?yàn)榭吹巾?xiàng)目中寫的還是有些問(wèn)題,網(wǎng)上go單例實(shí)現(xiàn)的搜索結(jié)果比較少經(jīng)測(cè)試也并不靠譜,所以在這記錄下

現(xiàn)狀

當(dāng)前有的項(xiàng)目直接使用Mutex鎖,有的就直接判斷nil則創(chuàng)建,對(duì)于前者,每次都加鎖性能差,對(duì)于后者則會(huì)出現(xiàn)多個(gè)實(shí)例,也就不是單例了

改進(jìn)

進(jìn)而想要改進(jìn)一下,在這不討論餓漢和線程非安全的實(shí)現(xiàn),對(duì)于go中線程安全的懶漢實(shí)現(xiàn),常見(jiàn)兩種:

雙重檢驗(yàn)sync.Once

雙重檢驗(yàn)示例:

package main
 
import (
    "sync"
    "testing"
)
var (
    instance *int
    lock      sync.Mutex
func getInstance() *int {
    if instance == nil {
        lock.Lock()
        defer lock.Unlock()
        if instance == nil {
            i := 1
            instance = &i
        }
    }
    return instance
}
// 用于下邊基準(zhǔn)測(cè)試
func BenchmarkSprintf(b *testing.B){
    for i:=0;i<b.N;i++{
        go getInstance()

是否線程安全

基于java中雙重檢驗(yàn)鎖的經(jīng)驗(yàn),因?yàn)閖vm的內(nèi)存模型,雙重檢驗(yàn)鎖會(huì)出現(xiàn)可見(jiàn)性問(wèn)題,可以通過(guò) volatile解決
那么在go里會(huì)有類似問(wèn)題嗎?
關(guān)鍵點(diǎn)在于instance變量的讀和寫是否是原子操作
這里做了個(gè)race競(jìng)態(tài)檢測(cè):

可以看到20行的寫入和14行的讀取發(fā)生了競(jìng)態(tài)
上例中用64位(系統(tǒng)是64位)的int指針表示一個(gè)實(shí)例,也說(shuō)明了對(duì)于64位數(shù)據(jù)的寫入和讀取是非原子操作

我們看另一種實(shí)現(xiàn):sync.Once方法

package main
 
import (
    "sync"
    "testing"
)
var (
    instance *int
    once      sync.Once
func getInstance() *int {
    once.Do(func(){
        if instance == nil {
            i := 1
            instance = &i
        }
    })
    return instance
}
func BenchmarkSprintf(b *testing.B){
    for i:=0;i<b.N;i++{
        go getInstance()
    }

實(shí)現(xiàn)比雙重檢驗(yàn)看起來(lái)要整潔許多

race檢測(cè)結(jié)果:

沒(méi)有發(fā)生競(jìng)態(tài)

關(guān)于sync.Once

那么sync.Once是怎么實(shí)現(xiàn)的呢

看下源碼:

package sync
 
import (
   "sync/atomic"
)
type Once struct {
   done uint32
   m    Mutex
}
func (o *Once) Do(f func()) {
   if atomic.LoadUint32(&o.done) == 0 {
      o.doSlow(f)
   }
func (o *Once) doSlow(f func()) {
   o.m.Lock()
   defer o.m.Unlock()
   if o.done == 0 {
      defer atomic.StoreUint32(&o.done, 1)
      f()

可以看到sync.Once內(nèi)部其實(shí)也是一個(gè)雙重檢驗(yàn)鎖,但是對(duì)于共享變量(done字段)的讀和寫使用了atomic包的StoreUint32和LoadUint32方法

sync.Once使用一個(gè)32位無(wú)符號(hào)整數(shù)表示共享變量,即使是32位變量的讀寫操作都需要atomic包方法來(lái)實(shí)現(xiàn)原子性,更說(shuō)明了go里邊指針的讀寫不能保證原子性

關(guān)于atomic和metex

引用一段話:https://ms2008.github.io/2019/05/12/golang-data-race/

解決 race 的問(wèn)題時(shí),無(wú)非就是上鎖??赡芎芏嗳硕悸?tīng)說(shuō)過(guò)一個(gè)高逼格的詞叫「無(wú)鎖隊(duì)列」。 都一聽(tīng)到加鎖就覺(jué)得很 low,那無(wú)鎖又是怎么一回事?其實(shí)就是利用 atomic 特性,那 atomic 會(huì)比 mutex 有什么好處呢?go race detector 的作者總結(jié)了這兩者的一個(gè)區(qū)別:
Mutexes do no scale. Atomic loads do.
mutex 由操作系統(tǒng)實(shí)現(xiàn),而 atomic 包中的原子操作則由底層硬件直接提供支持。在 CPU 實(shí)現(xiàn)的指令集里,有一些指令被封裝進(jìn)了 atomic 包,這些指令在執(zhí)行的過(guò)程中是不允許中斷(interrupt)的,因此原子操作可以在 lock-free 的情況下保證并發(fā)安全,并且它的性能也能做到隨 CPU 個(gè)數(shù)的增多而線性擴(kuò)展。
若實(shí)現(xiàn)相同的功能,后者通常會(huì)更有效率,并且更能利用計(jì)算機(jī)多核的優(yōu)勢(shì)。所以,以后當(dāng)我們想并發(fā)安全的更新一些變量的時(shí)候,我們應(yīng)該優(yōu)先選擇用 atomic 來(lái)實(shí)現(xiàn)。

結(jié)論

  • go單例實(shí)現(xiàn)—雙重檢測(cè)法對(duì)共享變量直接讀取和賦值是不安全的,需要atomic包實(shí)現(xiàn)原子操作的讀寫
  • 對(duì)于懶漢模式單例的實(shí)現(xiàn),sync.Once是更好的辦法,簡(jiǎn)潔安全,sync.Once已經(jīng)幫我們實(shí)現(xiàn)了安全的雙重檢驗(yàn),能做到加載完成后不再加鎖
  • 這里也提醒我們,只要是對(duì)于共享變量的并發(fā)訪問(wèn),一定要注意安全性,go更推崇避免共享變量,使用chan來(lái)交流信息,如果無(wú)法避免共享內(nèi)存,優(yōu)先使用atomic實(shí)現(xiàn),其次sync,安全第一!

到此這篇關(guān)于go單例實(shí)現(xiàn)雙重檢測(cè)是否安全的文章就介紹到這了,更多相關(guān)go單例雙重檢測(cè)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go-Web框架中AOP方案的實(shí)現(xiàn)方式

    Go-Web框架中AOP方案的實(shí)現(xiàn)方式

    本文主要介紹了Go-Web框架中AOP方案的實(shí)現(xiàn)方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Golang函數(shù)這些神操作你知道哪些

    Golang函數(shù)這些神操作你知道哪些

    這篇文章主要為大家介紹了一些Golang中函數(shù)的神操作,不知道你都知道哪些呢?文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,需要的可以參考一下
    2023-02-02
  • Golang中結(jié)構(gòu)體映射mapstructure庫(kù)深入詳解

    Golang中結(jié)構(gòu)體映射mapstructure庫(kù)深入詳解

    mapstructure用于將通用的map[string]interface{}解碼到對(duì)應(yīng)的 Go 結(jié)構(gòu)體中,或者執(zhí)行相反的操作。很多時(shí)候,解析來(lái)自多種源頭的數(shù)據(jù)流時(shí),我們一般事先并不知道他們對(duì)應(yīng)的具體類型。只有讀取到一些字段之后才能做出判斷
    2023-01-01
  • go語(yǔ)言靜態(tài)庫(kù)的編譯和使用方法

    go語(yǔ)言靜態(tài)庫(kù)的編譯和使用方法

    這篇文章主要介紹了go語(yǔ)言靜態(tài)庫(kù)的編譯和使用方法,本文以windows平臺(tái)為例,通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • go按行讀取文件的三種實(shí)現(xiàn)方式匯總

    go按行讀取文件的三種實(shí)現(xiàn)方式匯總

    最近有遇到需要用go讀取文件的情況,下面這篇文章主要給大家介紹了關(guān)于go按行讀取文件的三種實(shí)現(xiàn)方式,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • GO語(yǔ)言(golang)基礎(chǔ)知識(shí)

    GO語(yǔ)言(golang)基礎(chǔ)知識(shí)

    這篇文章主要介紹了GO語(yǔ)言(golang)基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-01-01
  • golang字符串轉(zhuǎn)64位整數(shù)的示例代碼

    golang字符串轉(zhuǎn)64位整數(shù)的示例代碼

    這篇文章主要介紹了golang字符串轉(zhuǎn)64位整數(shù),在Go語(yǔ)言中,可以使用strconv包中的ParseInt函數(shù)將字符串轉(zhuǎn)換為64位整數(shù),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-09-09
  • golang并發(fā)之使用sync.Pool優(yōu)化性能

    golang并發(fā)之使用sync.Pool優(yōu)化性能

    在Go提供如何實(shí)現(xiàn)對(duì)象的緩存池功能,常用一種實(shí)現(xiàn)方式是sync.Pool,?其旨在緩存已分配但未使用的項(xiàng)目以供以后重用,從而減輕垃圾收集器(GC)的壓力,下面我們就來(lái)看看具體操作吧
    2023-10-10
  • Go語(yǔ)言中defer使用的陷阱小結(jié)

    Go語(yǔ)言中defer使用的陷阱小結(jié)

    本文主要介紹了Go語(yǔ)言中defer使用的陷阱小結(jié),分別是defer語(yǔ)句不可以在return語(yǔ)句之后,defer語(yǔ)句執(zhí)行的匿名函數(shù),匿名函數(shù)的參數(shù)會(huì)被預(yù)先處理,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • go語(yǔ)言LeetCode題解720詞典中最長(zhǎng)的單詞

    go語(yǔ)言LeetCode題解720詞典中最長(zhǎng)的單詞

    這篇文章主要為大家介紹了go語(yǔ)言LeetCode題解720詞典中最長(zhǎng)的單詞,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12

最新評(píng)論