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

go?sync.Once實(shí)現(xiàn)高效單例模式詳解

 更新時(shí)間:2023年03月14日 09:15:43   作者:starrySky  
這篇文章主要為大家介紹了go?sync.Once實(shí)現(xiàn)高效單例模式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

1. 簡介

本文介紹使用sync.Once來實(shí)現(xiàn)單例模式,包括單例模式的定義,以及使用sync.Once實(shí)現(xiàn)單例模式的示例,同時(shí)也比較了其他單例模式的實(shí)現(xiàn)。最后以一個(gè)開源框架中使用sync.Once實(shí)現(xiàn)單例模式的例子來作為結(jié)尾。

2. 基本實(shí)現(xiàn)

2.1 單例模式定義

單例模式是一種創(chuàng)建型設(shè)計(jì)模式,它保證一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)全局訪問點(diǎn)來訪問這個(gè)實(shí)例。在整個(gè)應(yīng)用程序中,所有對(duì)于這個(gè)類的訪問都將返回同一個(gè)實(shí)例對(duì)象。

2.2 sync.Once實(shí)現(xiàn)單例模式

下面是一個(gè)簡單的示例代碼,使用 sync.Once 實(shí)現(xiàn)單例模式:

 package singleton
 import "sync"
 type singleton struct {
     // 單例對(duì)象的狀態(tài)
 }
 var (
     instance *singleton
     once     sync.Once
 )
 func GetInstance() *singleton {
     once.Do(func() {
         instance = &singleton{}
         // 初始化單例對(duì)象的狀態(tài)
     })
     return instance
 }

在上面的示例代碼中,我們定義了一個(gè) singleton 結(jié)構(gòu)體表示單例對(duì)象的狀態(tài),然后將它的實(shí)例作為一個(gè)包級(jí)別的變量 instance,并使用一個(gè) once 變量來保證 GetInstance 函數(shù)只被執(zhí)行一次。

GetInstance 函數(shù)中,我們使用 once.Do 方法來執(zhí)行一個(gè)初始化單例對(duì)象。由于 once.Do 方法是基于原子操作實(shí)現(xiàn)的,因此可以保證并發(fā)安全,即使有多個(gè)協(xié)程同時(shí)調(diào)用 GetInstance 函數(shù),最終也只會(huì)創(chuàng)建一個(gè)對(duì)象。

2.3 其他方式實(shí)現(xiàn)單例模式

2.3.1 全局變量定義時(shí)賦值,實(shí)現(xiàn)單例模式

在 Go 語言中,全局變量會(huì)在程序啟動(dòng)時(shí)自動(dòng)初始化。因此,如果在定義全局變量時(shí)給它賦值,則對(duì)象的創(chuàng)建也會(huì)在程序啟動(dòng)時(shí)完成,可以通過此來實(shí)現(xiàn)單例模式,以下是一個(gè)示例代碼:

type MySingleton struct {
    // 字段定義
}
var mySingletonInstance = &MySingleton{
    // 初始化字段
}
func GetMySingletonInstance() *MySingleton {
    return mySingletonInstance
}

在上面的代碼中,我們定義了一個(gè)全局變量 mySingletonInstance 并在定義時(shí)進(jìn)行了賦值,從而在程序啟動(dòng)時(shí)完成了對(duì)象的創(chuàng)建和初始化。在 GetMySingletonInstance 函數(shù)中,我們可以直接返回全局變量 mySingletonInstance,從而實(shí)現(xiàn)單例模式。

2.3.2 init 函數(shù)實(shí)現(xiàn)單例模式

在 Go 語言中,我們可以使用 init 函數(shù)來實(shí)現(xiàn)單例模式。init 函數(shù)是在包被加載時(shí)自動(dòng)執(zhí)行的函數(shù),因此我們可以在其中創(chuàng)建并初始化單例對(duì)象,從而保證在程序啟動(dòng)時(shí)就完成對(duì)象的創(chuàng)建。以下是一個(gè)示例代碼:

package main
type MySingleton struct {
    // 字段定義
}
var mySingletonInstance *MySingleton
func init() {
    mySingletonInstance = &MySingleton{
        // 初始化字段
    }
}
func GetMySingletonInstance() *MySingleton {
    return mySingletonInstance
}

在上面的代碼中,我們定義了一個(gè)包級(jí)別的全局變量 mySingletonInstance,并在 init 函數(shù)中創(chuàng)建并初始化了該對(duì)象。在 GetMySingletonInstance 函數(shù)中,我們直接返回該全局變量,從而實(shí)現(xiàn)單例模式。

2.3.3 使用互斥鎖實(shí)現(xiàn)單例模式

在 Go 語言中,可以只使用一個(gè)互斥鎖來實(shí)現(xiàn)單例模式。下面是一個(gè)簡單代碼的演示:

var instance *MySingleton
var mu sync.Mutex
func GetMySingletonInstance() *MySingleton {
   mu.Lock()
   defer mu.Unlock()
   if instance == nil {
      instance = &MySingleton{
         // 初始化字段
      }
   }
   return instance
}

在上面的代碼中,我們使用了一個(gè)全局變量instance來存儲(chǔ)單例對(duì)象,并使用了一個(gè)互斥鎖 mu 來保證對(duì)象的創(chuàng)建和初始化。具體地,我們在 GetMySingletonInstance 函數(shù)中首先加鎖,然后判斷 instance 是否已經(jīng)被創(chuàng)建,如果未被創(chuàng)建,則創(chuàng)建并初始化對(duì)象。最后,我們釋放鎖并返回單例對(duì)象。

需要注意的是,在并發(fā)高的情況下,使用一個(gè)互斥鎖來實(shí)現(xiàn)單例模式可能會(huì)導(dǎo)致性能問題。因?yàn)樵谝粋€(gè) goroutine 獲得鎖并創(chuàng)建對(duì)象時(shí),其他的 goroutine 都需要等待,這可能會(huì)導(dǎo)致程序變慢。

2.4 使用sync.Once實(shí)現(xiàn)單例模式的優(yōu)點(diǎn)

相對(duì)于init 方法和使用全局變量定義賦值單例模式的實(shí)現(xiàn),sync.Once 實(shí)現(xiàn)單例模式可以實(shí)現(xiàn)延遲初始化,即在第一次使用單例對(duì)象時(shí)才進(jìn)行創(chuàng)建和初始化。這可以避免在程序啟動(dòng)時(shí)就進(jìn)行對(duì)象的創(chuàng)建和初始化,以及可能造成的資源的浪費(fèi)。

而相對(duì)于使用互斥鎖實(shí)現(xiàn)單例模式,使用 sync.Once 實(shí)現(xiàn)單例模式的優(yōu)點(diǎn)在于更為簡單和高效。sync.Once提供了一個(gè)簡單的接口,只需要傳遞一個(gè)初始化函數(shù)即可。相比互斥鎖實(shí)現(xiàn)方式需要手動(dòng)處理鎖、判斷等操作,使用起來更加方便。而且使用互斥鎖實(shí)現(xiàn)單例模式需要在每次訪問單例對(duì)象時(shí)進(jìn)行加鎖和解鎖操作,這會(huì)增加額外的開銷。而使用 sync.Once 實(shí)現(xiàn)單例模式則可以避免這些開銷,只需要在第一次訪問單例對(duì)象時(shí)進(jìn)行一次初始化操作即可。

但是也不是說sync.Once便適合所有的場景,這個(gè)是需要具體情況具體分析的。下面說明sync.Onceinit方法,在哪些場景下使用init更好,在哪些場景下使用sync.Once更好。

2.5 sync.Once和init方法適用場景

對(duì)于init實(shí)現(xiàn)單例,比較適用于在程序啟動(dòng)時(shí)就需要初始化變量的場景。因?yàn)?code>init函數(shù)是在程序運(yùn)行前執(zhí)行的,可以確保變量在程序運(yùn)行時(shí)已經(jīng)被初始化。

對(duì)于需要延遲初始化某些對(duì)象,對(duì)象被創(chuàng)建出來并不會(huì)被馬上使用,或者可能用不到,例如創(chuàng)建數(shù)據(jù)庫連接池等。這時(shí)候使用sync.Once就非常合適。它可以保證對(duì)象只被初始化一次,并且在需要使用時(shí)才會(huì)被創(chuàng)建,避免不必要的資源浪費(fèi)。

3. gin中單例模式的使用

3.1 背景

這里首先需要介紹下gin.Engine, gin.Engine是Gin框架的核心組件,負(fù)責(zé)處理HTTP請(qǐng)求,路由請(qǐng)求到對(duì)應(yīng)的處理器,處理器可以是中間件、控制器或處理HTTP響應(yīng)等。每個(gè)gin.Engine實(shí)例都擁有自己的路由表、中間件棧和其他配置項(xiàng),通過調(diào)用其方法可以注冊路由、中間件、處理函數(shù)等。

一個(gè)HTTP服務(wù)器,只會(huì)存在一個(gè)對(duì)應(yīng)的gin.Engine實(shí)例,其保存了路由映射規(guī)則等內(nèi)容。

為了簡化開發(fā)者Gin框架的使用,不需要用戶創(chuàng)建gin.Engine實(shí)例,便能夠完成路由的注冊等操作,提高代碼的可讀性和可維護(hù)性,避免重復(fù)代碼的出現(xiàn)。這里對(duì)于一些常用的功能,抽取出一些函數(shù)來使用,函數(shù)簽名如下:

// ginS/gins.go
// 加載HTML模版文件
func LoadHTMLGlob(pattern string) {}
// 注冊POST請(qǐng)求處理器
func POST(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {}
// 注冊GET請(qǐng)求處理器
func GET(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {}
// 啟動(dòng)一個(gè)HTTP服務(wù)器
func Run(addr ...string) (err error) {}
// 等等...

接下來需要對(duì)這些函數(shù)來進(jìn)行實(shí)現(xiàn)。

3.2 具體實(shí)現(xiàn)

首先從使用出發(fā),這里使用POST方法/GET方法注冊請(qǐng)求處理器,然后使用Run方法啟動(dòng)服務(wù)器:

func main() {
   // 注冊u(píng)rl對(duì)應(yīng)的處理器
   POST("/login", func(c *gin.Context) {})
   // 注冊u(píng)rl對(duì)應(yīng)的處理器
   GET("/hello", func(c *gin.Context) {})
   // 啟動(dòng)服務(wù)
   Run(":8080")
}

這里我們想要的效果,應(yīng)該是調(diào)用Run方法啟動(dòng)服務(wù)后,往/login路徑發(fā)送請(qǐng)求,此時(shí)應(yīng)該執(zhí)行我們注冊的對(duì)應(yīng)處理器,往/hello路徑發(fā)送請(qǐng)求也是同理。

所以,這里POST方法,GET方法,Run方法應(yīng)該都是對(duì)同一個(gè)gin.Engine 進(jìn)行操作的,而不是各自使用各自的gin.Engine實(shí)例,亦或者每次調(diào)用就創(chuàng)建一個(gè)gin.Engine實(shí)例。這樣子才能達(dá)到我們預(yù)想的效果。

所以,我們需要實(shí)現(xiàn)一個(gè)方法,獲取gin.Engine實(shí)例,每次調(diào)用該方法都是獲取到同一個(gè)實(shí)例,這個(gè)其實(shí)也就是單例的定義。然后POST方法,GET方法又或者是Run方法,調(diào)用該方法獲取到gin.Engine實(shí)例,然后調(diào)用實(shí)例去調(diào)用對(duì)應(yīng)的方法,完成url處理器的注冊或者是服務(wù)的啟動(dòng)。這樣子就能夠保證是使用同一個(gè)gin.Engine實(shí)例了。具體實(shí)現(xiàn)如下:

// ginS/gins.go
import (
   "github.com/gin-gonic/gin"
)
var once sync.Once
var internalEngine *gin.Engine
func engine() *gin.Engine {
   once.Do(func() {
      internalEngine = gin.Default()
   })
   return internalEngine
}
// POST is a shortcut for router.Handle("POST", path, handle)
func POST(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
   return engine().POST(relativePath, handlers...)
}
// GET is a shortcut for router.Handle("GET", path, handle)
func GET(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
   return engine().GET(relativePath, handlers...)
}

這里engine() 方法使用了 sync.Once 實(shí)現(xiàn)單例模式,確保每次調(diào)用該方法返回的都是同一個(gè) gin.Engine 實(shí)例。然后POST/GET/Run方法通過該方法獲取到gin.Engine實(shí)例,然后調(diào)用實(shí)例中對(duì)應(yīng)的方法來完成對(duì)應(yīng)的功能,從而達(dá)到POST/GET/Run等方法都是使用同一個(gè)實(shí)例操作的效果。

3.3 sync.Once實(shí)現(xiàn)單例的好處

這里想要達(dá)到的目的,其實(shí)是GET/POST/Run等抽取出來的函數(shù),使用同一個(gè)gin.Engine實(shí)例。

為了達(dá)到這個(gè)目的,我們其實(shí)可以在定義internalEngine 變量時(shí),便對(duì)其進(jìn)行賦值;或者是通init函數(shù)完成對(duì)internalEngine變量的賦值,其實(shí)都可以。

但是我們抽取出來的函數(shù),用戶并不一定使用,定義時(shí)便初始化或者在init方法中便完成了對(duì)變量的賦值,用戶沒使用的話,創(chuàng)建出來的gin.Engine實(shí)例沒有實(shí)際用途,造成了不必要的資源的浪費(fèi)。

而engine方法使用sync.Once實(shí)現(xiàn)了internalEngin的延遲初始化,只有在真正使用到internalEngine時(shí),才會(huì)對(duì)其進(jìn)行初始化,避免了不必要的資源的浪費(fèi)。

這里其實(shí)也印證了上面我們所說的sync.Once的適用場景,對(duì)于不會(huì)馬上使用的單例對(duì)象,此時(shí)可以使用sync.Once來實(shí)現(xiàn)。

4.總結(jié)

單例模式是一種常用的設(shè)計(jì)模式,用于保證一個(gè)類僅有一個(gè)實(shí)例。在單例模式中,常常使用互斥鎖或者變量賦值的方式來實(shí)現(xiàn)單例。然而,使用sync.Once可以更方便地實(shí)現(xiàn)單例,同時(shí)也能夠避免了不必要的資源浪費(fèi)。當(dāng)然,沒有任何一種實(shí)現(xiàn)是適合所有場景的,我們需要根據(jù)具體場景具體分析。

以上就是go sync.Once實(shí)現(xiàn)高效單例模式詳解的詳細(xì)內(nèi)容,更多關(guān)于sync.Once單例模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺談Go Slice 高級(jí)實(shí)踐

    淺談Go Slice 高級(jí)實(shí)踐

    這篇文章主要介紹了淺談Go Slice 高級(jí)實(shí)踐,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • Go語言defer語句的三種機(jī)制整理

    Go語言defer語句的三種機(jī)制整理

    在本篇文章里小編給大家分享的是一篇關(guān)于Go語言defer語句的三種機(jī)制整理,需要的朋友們學(xué)習(xí)下吧。
    2020-03-03
  • 一文詳解Go語言中方法的指針接收者與值接收者

    一文詳解Go語言中方法的指針接收者與值接收者

    在 Go 語言里,方法是一種特殊的函數(shù),它與特定的類型關(guān)聯(lián),方法可以使用值接收者或者指針接收者,理解這兩種接收者的區(qū)別和使用場景,對(duì)于編寫高效、正確的 Go 代碼至關(guān)重要,本文將深入剖析指針接收者和值接收者的特點(diǎn),需要的朋友可以參考下
    2025-06-06
  • go?goroutine實(shí)現(xiàn)素?cái)?shù)統(tǒng)計(jì)的示例

    go?goroutine實(shí)現(xiàn)素?cái)?shù)統(tǒng)計(jì)的示例

    這篇文章主要介紹了go?goroutine實(shí)現(xiàn)素?cái)?shù)統(tǒng)計(jì),本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • Go Resiliency庫中timeout實(shí)現(xiàn)原理及源碼解析

    Go Resiliency庫中timeout實(shí)現(xiàn)原理及源碼解析

    Go-Resiliency庫中的timeout是一種基于協(xié)程的超時(shí)機(jī)制,通過創(chuàng)建協(xié)程來執(zhí)行任務(wù)并設(shè)置超時(shí)時(shí)間,若任務(wù)執(zhí)行時(shí)間超時(shí)則中止協(xié)程并返回錯(cuò)誤,需要詳細(xì)了解可以參考下文
    2023-05-05
  • Go 協(xié)程超時(shí)控制的實(shí)現(xiàn)

    Go 協(xié)程超時(shí)控制的實(shí)現(xiàn)

    本文主要介紹了Go 協(xié)程超時(shí)控制的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • golang?xorm?自定義日志記錄器之使用zap實(shí)現(xiàn)日志輸出、切割日志(最新)

    golang?xorm?自定義日志記錄器之使用zap實(shí)現(xiàn)日志輸出、切割日志(最新)

    這篇文章主要介紹了golang?xorm?自定義日志記錄器,使用zap實(shí)現(xiàn)日志輸出、切割日志,包括連接postgresql數(shù)據(jù)庫的操作方法及?zap日志工具?,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10
  • 自定義Go?Json的序列化方法譯文

    自定義Go?Json的序列化方法譯文

    這篇文章主要為大家介紹了自定義Go?Json序列化方法譯文,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • go goroutine 怎樣進(jìn)行錯(cuò)誤處理

    go goroutine 怎樣進(jìn)行錯(cuò)誤處理

    在 Go 語言程序開發(fā)中,goroutine 的使用是比較頻繁的,因此在日常編碼的時(shí)候 goroutine 里的錯(cuò)誤處理,怎么做會(huì)比較好呢,本文就來詳細(xì)介紹一下
    2021-07-07
  • Golang 并發(fā)控制模型的實(shí)現(xiàn)

    Golang 并發(fā)控制模型的實(shí)現(xiàn)

    Go控制并發(fā)有三種經(jīng)典的方式,使用?channel?通知實(shí)現(xiàn)并發(fā)控制、使用 sync 包中的?WaitGroup?實(shí)現(xiàn)并發(fā)控制、使用?Context?上下文實(shí)現(xiàn)并發(fā)控制,下面就來介紹一下
    2024-08-08

最新評(píng)論