單件模式——Golang實現

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

單件模式比較常見,算是建立型的設計模式,和原廠模式不同,他只能建立一個執行個體。他的應用情境很多,比如MySQL只能有一個執行個體這種都算。

單件模式能簡單分成支援並發和不支援並發兩種。不過並發這個很簡單,滿大街Golang實現的單件模式都是這樣的。

普通的單件模式

package singletonimport ("fmt")var _self *Singletontype Singleton struct {Name string}func Instance() *Singleton {if _self == nil {_self = new(Singleton)return _self}return _self}func (o *Singleton) SetName(s string) {_self.Name = s}func (o *Singleton) GetName() {fmt.Println("Name:", _self.Name)}

支援並發的單件模式也不算難,在這個基礎上增加一個叫做Double Check的處理。當年在去哪兒網面試被虐的時候被問到過這個,所以這個東西一直也都記著。

單件模式在並發情況下,上面的代碼就有問題了,有可能會被建立多次,上面的例子加個日誌:

var _self *Singletontype Singleton struct {Name string}func NewInstance(name string) *Singleton {fmt.Println("Create instance", name)time.Sleep(4 * time.Second)_self.Name = namereturn _self}func Instance(name string) *Singleton {if _self.Name == "" {return NewInstance(name)}return _self}func main() {_self = new(Singleton)go Instance("cyeam")go Instance("bryce")time.Sleep(10 * time.Second)fmt.Println(_self.Name)}

結果如下。例子裡面給執行個體化函數加了參數,方便判斷執行個體化的次數和結果。從下面的結果看來,無鎖的單件模式建立過兩次執行個體,第二次建立的執行個體覆蓋了第一個建立的執行個體。

Create instance cyeamCreate instance brycebryce

支援並發的單件模式

為了保證在並行調用的情況下只建立一個執行個體,就需要加鎖來保證串列建立。簡單粗暴的方法就是Instance()方法直接全部上鎖。

var _self *Singletontype Singleton struct {Name stringsync.Mutex}func NewInstance(name string) *Singleton {fmt.Println("Create instance", name)time.Sleep(4 * time.Second)_self.Name = namereturn _self}func Instance(name string) *Singleton {_self.Mutex.Lock()defer _self.Mutex.Unlock()if _self.Name == "" {return NewInstance(name)}return _self}func main() {_self = new(Singleton)_self.Mutex = sync.Mutex{}go Instance("cyeam")go Instance("bryce")time.Sleep(10 * time.Second)fmt.Println(_self.Name)}

簡單粗暴的加鎖,可以發現能夠解決並發多次建立的問題。但是如此一來,整個建立流程就變成串列調用了。比如有1000次建立請求,只要建立一個執行個體就好,剩下999次完全沒有必要加鎖,直接將之前第一個建立的執行個體返回就好。而這樣的寫法會導致每一次都是帶鎖訪問,影響速度。

Create instance cyeamcyeam

接著上面的思路,我們可以不為這個函數整體加鎖,在建立的時候加鎖即可。

func Instance(name string) *Singleton {if _self.Name == "" {_self.Mutex.Lock()defer _self.Mutex.Unlock()return NewInstance(name)}return _self}

在判斷確認對象為空白後,開始建立對象,結果如下:

Create instance cyeamCreate instance brycebryce

雖然已經加上了鎖,但是可以看到,依然建立了兩次對象。如果兩個並行的建立調用,此時if _self.Name == ""這個調用也同時執行,自然也都是true,接著,雖然有鎖,但是也就是串列得去建立對象,外面的if在無鎖的情況是失效了。

這時就需要牛逼的Double Check了,在鎖裡面再加一次判空檢查。

func Instance(name string) *Singleton {if _self.Name == "" {_self.Mutex.Lock()defer _self.Mutex.Unlock()if _self.Name == "" {return NewInstance(name)}}return _self}

結果如下。如此一來,既兼顧了效率,又能夠很好的支援並發。

Create instance cyeamcyeam

上面寫的這些都是傳統的寫法,對於一般語言使用的,對於我大Golang,還有一個更簡單的實現。

import ("fmt""sync""time")var _self *Singletontype Singleton struct {Name stringsync.Once}func NewInstance(name string) *Singleton {fmt.Println("Create instance", name)time.Sleep(4 * time.Second)_self.Name = namereturn _self}func Instance(name string) *Singleton {if _self.Name == "" {_self.Once.Do(func() { NewInstance(name) })}return _self}func main() {_self = new(Singleton)_self.Once = sync.Once{}go Instance("cyeam")go Instance("bryce")time.Sleep(10 * time.Second)fmt.Println(_self.Name)}

本文所涉及到的完整源碼請參考。

參考文獻
  1. 設計模式(2) - Singleton單件模式 - shltsh

原文連結:單件模式——Golang實現,轉載請註明來源!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.