返回

Golang sync.Once 用法解析——只需初始化一次

后端

sync.Once 用法

sync.Once 的用法非常简单,它只有一个方法:Do。Do 方法接收一个函数作为参数,当且仅当 sync.Once 对象尚未被调用时,该函数才会被执行。

以下代码演示了如何使用 sync.Once:

package main

import (
	"fmt"
	"sync"
)

var once sync.Once

func main() {
	once.Do(func() {
		fmt.Println("Hello, world!")
	})
}

运行以上代码,输出结果为:

Hello, world!

可以看出,Do 方法只被执行了一次,即使我们多次调用它。这是因为 sync.Once 会在内部维护一个状态,用来记录它是否已经被调用过。

sync.Once 原理

sync.Once 的原理也很简单,它使用了一个原子变量来记录它的状态。原子变量是一个只能被一个 goroutine 同时访问的变量。这意味着,即使有多个 goroutine同时调用 Do 方法,也只有一个 goroutine 能够成功执行 Do 方法中的函数。

以下代码展示了 sync.Once 的内部实现:

type Once struct {
	m    sync.Mutex
	done uint32
}

func (o *Once) Do(f func()) {
	if o.done == 0 {
		o.m.Lock()
		if o.done == 0 {
			defer o.m.Unlock()
			f()
			o.done = 1
		} else {
			o.m.Unlock()
		}
	}
}

可以看到,sync.Once 使用了一个 sync.Mutex 来保证 Do 方法的原子性。当 Do 方法被调用时,它会先检查 done 字段是否为 0。如果 done 字段为 0,则表示 Do 方法尚未被调用过,于是它会使用 sync.Mutex 来锁定 o.done 字段,然后再次检查 done 字段是否为 0。如果 done 字段仍然为 0,则表示 Do 方法确实尚未被调用过,于是它会执行 Do 方法中的函数,并将 done 字段设置为 1,表示 Do 方法已经执行过。如果 done 字段不为 0,则表示 Do 方法已经执行过,于是它会直接返回,而不执行 Do 方法中的函数。

sync.Once 应用

sync.Once 可以用于多种场景,比如:

  • 初始化全局变量 :sync.Once 可以用来初始化全局变量,确保全局变量只被初始化一次。
  • 实现单例模式 :sync.Once 可以用来实现单例模式,确保只有一个实例被创建。
  • 防止重复操作 :sync.Once 可以用来防止重复操作,比如防止多次打开同一个文件。

结语

sync.Once 是一个非常有用的同步机制,它可以确保某项操作只被执行一次。这在并发编程中非常有用,可以防止多个 goroutine 同时执行相同的操作。

希望本文对您有所帮助。如果您有任何问题,欢迎在评论区留言。