最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang使用sync.Once實現(xiàn)懶加載的用法和坑點詳解

 更新時間:2023年11月05日 13:55:16   作者:utmhikari  
這篇文章主要為大家詳細介紹了golang使用sync.Once實現(xiàn)懶加載的用法和坑點,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

在使用Golang做后端開發(fā)的工程中,我們通常需要聲明一些一些配置類或服務單例等在業(yè)務邏輯層面較為底層的實例。為了節(jié)省內(nèi)存或是冷啟動開銷,我們通常采用lazy-load懶加載的方式去初始化這些實例。初始化單例這個行為是一個非常經(jīng)典的并發(fā)處理的案例,比如在java當中,我們可能用到建立雙重鎖+volatile的方式保證初始化邏輯只被訪問一次,并且所有線程最終都可以讀取到初始化完成的實例產(chǎn)物。這段經(jīng)典的代碼可以按如下的方式編寫:

public class Singleton {
    private volatile static Singleton uniqueSingleton;
 
    private Singleton() {
    }
 
    public Singleton getInstance() {
        if (null == uniqueSingleton) {
            synchronized (Singleton.class) {
                if (null == uniqueSingleton) {
                    uniqueSingleton = new Singleton();
                }
            }
        }
        return uniqueSingleton;
    }
}

但在Golang里面,實現(xiàn)懶加載的方式可以簡單的多,用內(nèi)置的sync.Once就能滿足。假設我們有一個user單例,需要被1000個線程讀取并打印,就可以這樣子寫:

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

var user *User
var userOnce sync.Once

func initUser() {
    user = &User{}
    cfgStr := `{"name":"foobar","age":18}`
    if err := json.Unmarshal([]byte(cfgStr), user); err != nil {
        panic("load user err: " + err.Error())
    }
}

func getUser() *User {
    userOnce.Do(initUser)
    return user
}

func TestSyncOnce(t *testing.T) {
    var wg sync.WaitGroup
    for i := 1; i < 1000; i++ {
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            curUser := getUser()
            t.Logf("[%d] got user: %+v", n, curUser)
        }(i)
    }
    wg.Wait()
}

這段代碼里,首先是通過var userOnce sync.Once聲明了一個sync.Once實例,然后在getUser當中,我們聲明了userOnce.Do(initUser)這個操作。假設一個goroutine最先到達這個操作,就會上鎖并執(zhí)行initUser,其它goroutine到達之后,得等第一個goroutine執(zhí)行完initUser之后,才會繼續(xù)return user。這樣,就能一來保證initUser只會執(zhí)行一次,二來所有goroutine都能夠最終讀到初始化完成的user單例。

sync.Once的工作機理也很簡單,通過一個鎖和一個flag就能夠實現(xiàn):

func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 0 { // 如果是1表示已經(jīng)完成了,跳過
		o.doSlow(f)
	}
}

func (o *Once) doSlow(f func()) {
	o.m.Lock() // 只有1個goroutine能拿到鎖,其它的等待
	defer o.m.Unlock()
	if o.done == 0 { // 如果還是0表示第一個來的,不是0就表示已經(jīng)有goroutine做完了
		defer atomic.StoreUint32(&o.done, 1)
		f()
	}
}

最后也需要注意,sync.Once使用上面有一個坑點,不能也不需要像java一樣為單例提前做nil判斷。比如下面一段代碼是有問題的:

func initUser() {
    user = &User{} // 先給一個zero-value實例
    cfgStr := `{"name":"foobar","age":18}` // 然后加載json內(nèi)容,完成初始化
    if err := json.Unmarshal([]byte(cfgStr), user); err != nil {
        panic("load user err: " + err.Error())
    }
}

func getUser() *User {
    if user == nil {
        userOnce.Do(initUser)
    }
    return user
}

由于Golang沒有volatile關鍵字,不能控制單例在內(nèi)存的可見性,那么多goroutine并發(fā)時,就有可能出現(xiàn)這樣的執(zhí)行時序:

  • goroutine-A過了getUser的user == nil判斷,進入到了initUser邏輯,走到了cfgStr := XXX一行
  • 此時切換到goroutine-B,因為goroutine-A在initUser已經(jīng)走過了user = &User{}一行,所以跳過了user == nil判斷,直接返回沒有完全初始化的user實例,然后一直往下運行,就沒切回給goroutine-A

這樣的結果,就導致有goroutine拿到未初始化完成的實例往后運行,后面就出問題了。所以實戰(zhàn)當中需要留意,用sync.Once時,不能也不需要加這些nil判斷,就能滿足懶加載單例/配置之類的邏輯。

到此這篇關于golang使用sync.Once實現(xiàn)懶加載的用法和坑點詳解的文章就介紹到這了,更多相關go sync.Once懶加載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關文章

  • Go實現(xiàn)并發(fā)的示例代碼

    Go實現(xiàn)并發(fā)的示例代碼

    Go語言的并發(fā)機制是其強大和流行的一個關鍵特性之一,本文主要介紹了Go實現(xiàn)并發(fā)的示例代碼,具有一定的參考價值,感興趣的可以了解一下
    2023-11-11
  • golang gin ShouldBind的介紹和使用示例詳解

    golang gin ShouldBind的介紹和使用示例詳解

    在 Go 語言的 Gin 框架中,ShouldBind 是用于將請求中的數(shù)據(jù)綁定到結構體的一個方法,它簡化了從請求中提取參數(shù)的過程,支持多種數(shù)據(jù)格式,下面給大家分享golang gin ShouldBind的介紹和使用示例,感興趣的朋友一起看看吧
    2024-10-10
  • 詳解Go語言中泛型的實現(xiàn)原理與使用

    詳解Go語言中泛型的實現(xiàn)原理與使用

    本文是對泛型的基本思想及其在?Go?中的實現(xiàn)的一個比較容易理解的介紹,同時也是對圍繞泛型的各種性能討論的簡單總結,感興趣的可以學習一下
    2022-05-05
  • Go語言滑動窗口最大值的實現(xiàn)示例

    Go語言滑動窗口最大值的實現(xiàn)示例

    本文主要介紹了Go語言滑動窗口最大值的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-08-08
  • Golang中panic的實現(xiàn)示例

    Golang中panic的實現(xiàn)示例

    這篇文章主要介紹了Golang中panic的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-06-06
  • Go defer 原理和源碼剖析(推薦)

    Go defer 原理和源碼剖析(推薦)

    這篇文章主要介紹了Go defer 原理和源碼剖析,需要的朋友可以參考下
    2021-11-11
  • Golang極簡入門教程(三):并發(fā)支持

    Golang極簡入門教程(三):并發(fā)支持

    這篇文章主要介紹了Golang極簡入門教程(三):并發(fā)支持,本文講解了goroutine線程、channel 操作符等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • Go語言數(shù)據(jù)結構之二叉樹可視化詳解

    Go語言數(shù)據(jù)結構之二叉樹可視化詳解

    這篇文章主要為大家詳細介紹了Go語言數(shù)據(jù)結構中二叉樹可視化的方法詳解,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2022-09-09
  • Go調(diào)度器學習之協(xié)作與搶占詳解

    Go調(diào)度器學習之協(xié)作與搶占詳解

    如果某個G執(zhí)行時間過長,其他的G如何才能被正常調(diào)度,這就引出了接下來的話題:協(xié)作與搶占。本文將通過一些示例為大家詳細講講調(diào)度器中協(xié)作與搶占的相關知識,需要的可以參考一下
    2023-04-04
  • go語言channel實現(xiàn)多核并行化運行的方法

    go語言channel實現(xiàn)多核并行化運行的方法

    這篇文章主要介紹了go語言channel實現(xiàn)多核并行化運行的方法,實例分析了channel實現(xiàn)多核并行化運行的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-03-03

最新評論

长葛市| 阿坝县| 洛南县| 巩留县| 垣曲县| 陇南市| 新丰县| 武强县| 沈阳市| 蕉岭县| 阜宁县| 冷水江市| 桦南县| 汶上县| 巴中市| 电白县| 蒲城县| 临泽县| 道真| 弥勒县| 滕州市| 盘锦市| 武功县| 志丹县| 嘉黎县| 青田县| 九龙县| 灌云县| 玉屏| 辉县市| 肇源县| 赤城县| 湄潭县| 内丘县| 东兰县| 普陀区| 宣恩县| 陈巴尔虎旗| 松阳县| 闽清县| 团风县|