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

Go中sync.Once源碼的深度講解

 更新時(shí)間:2025年01月28日 09:53:08   作者:gopher_looklook  
sync.Once是Go語言標(biāo)準(zhǔn)庫中的一個(gè)同步原語,用于確保某個(gè)操作只執(zhí)行一次,本文將從源碼出發(fā)為大家詳細(xì)介紹一下sync.Once的具體使用,x希望對大家有所幫助

概念

sync.Once是Go語言標(biāo)準(zhǔn)庫中的一個(gè)同步原語,用于確保某個(gè)操作只執(zhí)行一次。它在多線程環(huán)境中非常有用,尤其是在需要初始化共享資源或執(zhí)行某些一次性任務(wù)時(shí)。

簡單示例

當(dāng)我們在web服務(wù)訪問某個(gè)路由時(shí),如果需要事先獲取某些配置,往往會寫一個(gè)loadConfig函數(shù),獲取一個(gè)cfg配置項(xiàng)。多次路由訪問所需要獲取的配置項(xiàng)通常是相同的,如果對于每次路由訪問,都加載一次loadConfig函數(shù),會導(dǎo)致產(chǎn)生一些不必要的開銷。如果loadConfig涉及到讀取文件、解析配置、網(wǎng)絡(luò)請求時(shí),有可能會額外增加的請求響應(yīng)時(shí)間,降低服務(wù)的吞吐量。使用sync.Once包提供的Do函數(shù),就可以只在第一次請求時(shí)調(diào)用loadConfig函數(shù)加載配置,之后的請求都復(fù)用第一次請求的配置,縮短響應(yīng)時(shí)間。

package main

import (
    "log"
    "net/http"
    "sync"
)

type Config struct {
    APIKey   string
    LogLevel string
}

var (
    config *Config
    once   sync.Once
)

func loadConfig() {
    // 模擬從文件或環(huán)境變量加載配置
    config = &Config{
       APIKey:   "secret-key",
       LogLevel: "debug",
    }
    log.Println("Configuration loaded")
}

func GetConfig() *Config {
    once.Do(loadConfig) // 僅第一次訪問時(shí)會執(zhí)行l(wèi)oadConfig函數(shù)
    return config
}

func handler(w http.ResponseWriter, r *http.Request) {
    cfg := GetConfig()
    log.Printf("Request handled with API key: %s", cfg.APIKey)
    w.Write([]byte("OK"))
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8888", nil))
}

源碼解讀

源碼文件:src/sync/once.go (go 1.23 版本)

package sync

import (
    "sync/atomic"
)

type Once struct {
    done atomic.Uint32 // 是否已執(zhí)行標(biāo)識位,0-未執(zhí)行 1-已執(zhí)行
    m    Mutex         // 互斥鎖,確保并發(fā)安全
}

func (o *Once) Do(f func()) {
    // 第一次執(zhí)行Do函數(shù)時(shí),原子操作檢查o.done==0,執(zhí)行doSlow函數(shù)后,o.done==1
    // 第二次及之后執(zhí)行Do函數(shù),原子操作檢查o.done標(biāo)識位為1,Do函數(shù)不執(zhí)行任何功能,確保了f函數(shù)只在第一次被執(zhí)行
    if o.done.Load() == 0 {
       o.doSlow(f) // 調(diào)用doSlow函數(shù)執(zhí)行f方法。第一次執(zhí)行時(shí),同一時(shí)間可能有多個(gè)goroutine嘗試同時(shí)執(zhí)行doSlow函數(shù)
    }
}

func (o *Once) doSlow(f func()) {
    o.m.Lock() // 加鎖保護(hù),避免多個(gè)goroutine同時(shí)繞過之前的原子操作檢查,并發(fā)修改o.done的值
    defer o.m.Unlock()
    // 二次檢查o.done的值,同一時(shí)間并發(fā)執(zhí)行doSlow函數(shù)的goroutine,在第一個(gè)goroutine將o.done置為1并解除互斥鎖后,
    // 剩下的goroutine識別到自身的o.done已經(jīng)被設(shè)為1,無法繞過二次檢查
    if o.done.Load() == 0 { 
       defer o.done.Store(1) // 需要在f()函數(shù)執(zhí)行完成之后,原子性地將o.done設(shè)為1
       f() // 執(zhí)行f方法,一定只有一個(gè)goroutine會調(diào)用這個(gè)方法
    }
}

可以看到,once.go文件的代碼非常精煉。僅定義了一個(gè)含2個(gè)非導(dǎo)出字段donem的結(jié)構(gòu)體Once,并提供了一個(gè)doSlow方法用于執(zhí)行f函數(shù)。當(dāng)我們調(diào)用Do方法時(shí),程序經(jīng)歷了幾個(gè)關(guān)鍵步驟:

  • 判斷done標(biāo)志位是否等于0,如果是,說明f函數(shù)還沒有被執(zhí)行,執(zhí)行doSlow方法
  • mu互斥鎖加鎖,防止多個(gè)goroutine并發(fā)操作
  • double-check done標(biāo)志位是否等于0,如果是,說明f函數(shù)還沒有被執(zhí)行,執(zhí)行f函數(shù)
  • f函數(shù)執(zhí)行完成之后,再將done標(biāo)志位原子性設(shè)為1。使用原子操作是從內(nèi)存可見性的角度出發(fā),如果done使用uint32而不是atomic.Uint32done修改可能不會立即被其它goroutine感知,解鎖后仍有可能存在goroutinedone等于0,重復(fù)執(zhí)行f函數(shù)
  • mu互斥鎖解鎖。此時(shí)進(jìn)入到doSlow函數(shù)的其它goroutine也感知到了o.done等于1,不會重復(fù)執(zhí)行f函數(shù)了

總結(jié)

上文就是針對Go源碼sync.Once原理和使用方式的講解。在實(shí)際開發(fā)中,sync.Once的使用還是非常普遍的。掌握sync.Once的底層原理,有助于我們在今后的開發(fā)中更有把握地利用它永遠(yuǎn)只執(zhí)行一次函數(shù)的特性,完成復(fù)雜的技術(shù)需求或者業(yè)務(wù)需求。

到此這篇關(guān)于Go中sync.Once源碼的深度講解的文章就介紹到這了,更多相關(guān)Go sync.Once內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • golang gorm 結(jié)構(gòu)體的表字段缺省值設(shè)置方式

    golang gorm 結(jié)構(gòu)體的表字段缺省值設(shè)置方式

    這篇文章主要介紹了golang gorm 結(jié)構(gòu)體的表字段缺省值設(shè)置方式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go 中閉包的底層原理

    Go 中閉包的底層原理

    這篇文章主要介紹了Go 中閉包的底層原理,閉包的基本原理是一種現(xiàn)象,一個(gè)函數(shù)內(nèi)引用了外部的局部變量的現(xiàn)象,帶著些許的了解和小編一起進(jìn)入文章正題學(xué)習(xí)
    2021-10-10
  • GO語言基本類型分析

    GO語言基本類型分析

    這篇文章主要介紹了GO語言基本類型,較為詳細(xì)的分析了整形、浮點(diǎn)型、字符串、指針等類型的具體用法,是深入學(xué)習(xí)GO語言所必須掌握的重要基礎(chǔ),需要的朋友可以參考下
    2014-12-12
  • Go語言中map使用和并發(fā)安全詳解

    Go語言中map使用和并發(fā)安全詳解

    golang?自帶的map不是并發(fā)安全的,并發(fā)讀寫會報(bào)錯(cuò),所以下面這篇文章主要給大家介紹了關(guān)于Go語言中map使用和并發(fā)安全的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • Golang字符串拼接的性能以及原理詳解

    Golang字符串拼接的性能以及原理詳解

    最近在做性能優(yōu)化,有個(gè)函數(shù)里面的耗時(shí)特別長,看里面的操作大多是一些字符串拼接的操作,而字符串拼接在golang里面其實(shí)有很多種實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于Golang字符串拼接的性能以及原理的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • Golang讀寫二進(jìn)制文件方法總結(jié)

    Golang讀寫二進(jìn)制文件方法總結(jié)

    使用?Golang?的?encoding/gob?包讀寫二進(jìn)制文件非常方便,而且代碼量也非常少,本文就來通過兩個(gè)示例帶大家了解一下encoding/gob的具體用法吧
    2023-05-05
  • Go設(shè)計(jì)模式之訪問者模式講解和代碼示例

    Go設(shè)計(jì)模式之訪問者模式講解和代碼示例

    訪問者是一種行為設(shè)計(jì)模式, 允許你在不修改已有代碼的情況下向已有類層次結(jié)構(gòu)中增加新的行為,本文將通過代碼示例給大家詳細(xì)的介紹一下Go設(shè)計(jì)模式之訪問者模式,需要的朋友可以參考下
    2023-08-08
  • goland服務(wù)熱重啟的配置文件

    goland服務(wù)熱重啟的配置文件

    這篇文章主要介紹了goland服務(wù)熱重啟的配置文件,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • go語言-在mac下brew升級golang

    go語言-在mac下brew升級golang

    這篇文章主要介紹了go語言-在mac下brew升級golang,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 詳解玩轉(zhuǎn)直播系列之消息模塊演進(jìn)

    詳解玩轉(zhuǎn)直播系列之消息模塊演進(jìn)

    本篇文章針對秀場直播,簡單地描述一下消息模型,說明一下我們消息模型的架構(gòu),并結(jié)合我們一年以來,通過處理不同的業(yè)務(wù)線上問題,來進(jìn)行演進(jìn)式的消息模型架構(gòu)的升級與調(diào)整,將此整理成文,并分享給大家
    2021-06-06

最新評論