golang sync.Once单例模式介绍

  • Post author:
  • Post category:golang




golang的单例模式介绍

  1. 首先在程序开发当中我们都会发现有很多设计模式,那么我们今天介绍的是最常用的单例模式,最近刚好有时时间,和大家一起学习一下!!!


单例模式我们可以自己实现,当前最简单的办法是通过bool类型来判断当然这个是很简单的啦 ,包括可以通过计数方式不做描述(有兴趣可以评论或者私信我看到会回复,要注意并发哦!!!)



那么接下来今天的主场来啦(sync.Once):

  1. sync.Once 相信各位都不陌生,在go 语言里很多场景会用到sync,包括我们的单例模式;接下来我们一起看看:
// Once is an object that will perform exactly one action.
//
// A Once must not be copied after first use.
type Once struct {
	// done indicates whether the action has been performed.
	// It is first in the struct because it is used in the hot path.
	// The hot path is inlined at every call site.
	// Placing done first allows more compact instructions on some architectures (amd64/386),
	// and fewer instructions (to calculate offset) on other architectures.
	done uint32
	m    Mutex
}

以上是Once的一个结构体 上面标注的也很清楚 Once里面包含俩个属性 一个是

done

另外一个是

Mutex

也是比较简单啦,其中done 是存储是否已经执行的数字来标识,我们可以看看他的俩个实现方法

func (o *Once) Do(f func()) {
	// Note: Here is an incorrect implementation of Do:
	//
	//	if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
	//		f()
	//	}
	//
	// Do guarantees that when it returns, f has finished.
	// This implementation would not implement that guarantee:
	// given two simultaneous calls, the winner of the cas would
	// call f, and the second would return immediately, without
	// waiting for the first's call to f to complete.
	// This is why the slow path falls back to a mutex, and why
	// the atomic.StoreUint32 must be delayed until after f returns.

	if atomic.LoadUint32(&o.done) == 0 {
		// Outlined slow-path to allow inlining of the fast-path.
		o.doSlow(f)
	}
}

func (o *Once) doSlow(f func()) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.done == 0 {
		defer atomic.StoreUint32(&o.done, 1)
		f()
	}
}

其中我们可以看到很简单的俩个函数 其中

Do(func())

就是我们最常用的一个单例,我们看一下 里面是利用 原子操作直接对他的地址进行load 获取实际的值(对原子操作不了解的 我们后续会对该包进行讲解,这次只对sync.One 不做拓展),load出来后一个if判断 如果 ==0 那么代表没有被执行过 然后调用

doSlow(f func())

函数,反之直接返回;

那我们接下来看看doSlow 这个里面有一个互斥锁,进来的时候进行了一个Lock 这里加锁的目的是为了防止并发调用Do 函数并且当次并为执行到atomic.StoreUint32(&o.done, 1) 所以在这里加了互斥锁方式次执行;此时加了锁那么这个地方只有一个任务进来调度, 这个时候依然进行了对done==0的判断(这个地方有些同学可能会觉得不太必要,其实是很有必要的 比如并发的时候俩个任务同时进来第一个获取到锁 第二个在doSlow阻塞,这个时候只要第一个释放后第二个任务就会立即执行重复执行f()这个函数);接下来就是一个f()函数执行,然后执行defer 通过原子操作把done 赋值为1;然后释放锁,这个时候整个流程就是一个单例下次再调用的时候会直接被返回。


到此今天的分享结束啦!后续会围绕这次的分享做切入点来聊一聊 原子包atomic 和 Mutex 希望大家相互学习!!!!



版权声明:本文为qq_36546907原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。