Eq's Blog

sync.Once with err

带有返回值的 sync.Once


预计阅读时间 2分钟

type ErrOnce struct {
	done uint32
	m    sync.Mutex
}

func (o *ErrOnce) Do(f func() error) error {
	if atomic.LoadUint32(&o.done) == 0 {
		return o.doSlow(f)
	}
	return nil
}

func (o *ErrOnce) doSlow(f func() error) error {
	o.m.Lock()
	defer o.m.Unlock()
	//  因为已经加了锁,所以不需要原子读写
	if o.done == 0 {
		err := f()
		if err != nil {
			return err
		}
		o.done = 1
	}
	return nil
}

以下引用自极客兔兔 https://geektutu.com/post/hpg-sync-once.html

将 done 置为 Once 的第一个字段:done 在热路径中,done 放在第一个字段,能够减少 CPU 指令,也就是说,这样做能够提升性能。

简单解释下这句话:

热路径(hot path)是程序非常频繁执行的一系列指令,sync.Once 绝大部分场景都会访问 o.done,在热路径上是比较好理解的,如果 hot path >编译后的机器码指令更少,更直接,必然是能够提升性能的。

为什么放在第一个字段就能够减少指令呢?因为结构体第一个字段的地址和结构体的指针是相同的,如果是第一个字段,直接对结构体的指针解引用即>可。如果是其他的字段,除了结构体指针外,还需要计算与第一个值的偏移(calculate offset)。在机器码中,偏移量是随指令传递的附加值,CPU >需要做一次偏移值与指针的加法运算,才能获取要访问的值的地址。因为,访问第一个字段的机器代码更紧凑,速度更快。