go sync.Once源码分析

适用场景

服务启动时读取全局配置。
单个函数流程里面只调用一次。

源码

双重检查done值是0后,加锁执行指定函数并把done值改成1。

type Once struct {
	done uint32
	m    Mutex
}

func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 0 {
		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()
	}
}

源码使用Mutex的原因

package main

import "sync/atomic"

type Once struct {
	done uint32
}

func (o *Once) Do(f func()) {
	if !atomic.CompareAndSwapUint32(&o.done, 0, 1) {
		return
	}
	f()
}

相比于源码,直接使用cas的问题是,第1个goroutine在成功执行cas后没有执行完指定函数,其他goroutine因cas失败而返回后,使用未初始化的资源时可能会panic。
第1个goroutine结束前,阻塞住其他goroutine,确保所有goroutine可以正常访问资源。

posted on 2023-01-21 17:43  王景迁  阅读(16)  评论(0编辑  收藏  举报

导航