日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Go單例模式與Once源碼實現_Golang

作者:如雨隨行2020 ? 更新時間: 2023-01-05 編程語言

單例實現

type singleton struct{}

var (
	instance    *singleton
	initialized uint32
	mu          sync.Mutex
)

func Instance() *singleton {
	if atomic.LoadUint32(&initialized) == 1 {
		return instance
	}

	mu.Lock()
	defer mu.Unlock()

	if instance == nil {
		defer atomic.StoreUint32(&initialized, 1)
		instance = &singleton{}
	}
	return instance
}

其中通用的代碼提取出來,就成了標準庫中sync.Once的實現:

type Once struct {
	done uint32
	m    sync.Mutex
}

func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 0 {

		o.m.Lock()
		defer o.m.Unlock()

		if o.done == 0 {
			defer atomic.StoreUint32(&o.done, 1)
			f()
		}
	}
}

于是,使用sync.Once重新實現單例模式

var (
	instance2 *singleton
	once sync.Once
)

func Instance2() *singleton {
	once.Do(func() {
		instance2 = &singleton{}
	})
	return instance2
}

sync.Once源碼分析

1. lock并不會同步值

在lock和unlock之間修改值,并不會保證對其他協程是可見的,除非使用相同的Mutex加鎖,想要同步值必須使用atomic;

lock可以通過串行化,使得兩個協程的操作存在happen-before關系,從而是的操作可見

happen-before原則定義如下:

如果一個操作happens-before(之前發生)另一個操作,那么第一個操作的執行結果將對第二個操作可見,而且第一個操作的執行順序排在第二個操作之前。

兩個操作之間存在happens-before關系,并不意味著一定要按照happens-before原則制定的順序來執行。如果重排序之后的執行結果與按照happens-before關系來執行的結果一致,那么這種重排序并不非法。

2. Do執行一次

當第一次執行完Do之后,done設置成1,后面執行Do會直接跳過

3. Once執行Do后不準copy

A Once must not be copied after first use.

sync.Once執行完Dodone已經設置成1了,copy出來的once執行Do會直接跳過

4. Do并發時阻塞

當兩個或者多個協程同時調用Do時,先調用的協程執行,后面的協程會阻塞;

解釋:以單例使用once的實現說明,兩個協程同時調用Instance2(),先調用的協程執行創建并拿到返回值,后調用的阻塞,

? 等到先調用的完成后再拿到返回值;

意義:這樣的好處是防止后調用的協程拿到的是nil

源碼說明:上面第二段代碼13行使用defer,要等f()結束才會把done設置成1;其他協程并發調用Do時,done==0

? 然后請求m.Lock()形成阻塞

5. Do遞歸死鎖

如果Do中的方法調用當前once的Do會造成死鎖,原因參考上面一點(sync.Mutex.Lock()時不可重入鎖)

  • 《Go語言高級編程》
  • Go1.16源碼

原文鏈接:https://blog.csdn.net/qq_22038259/article/details/128123515

欄目分類
最近更新