sync.Once 用于确保某个函数只执行一次,无论有多少个 goroutine 尝试执行它。它有一个方法 Do(f func()),传入需要执行的函数。

基本用法示例:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var once sync.Once
    done := make(chan bool)
    
    // 模拟多个 goroutine 调用
    for i := 0; i < 10; i++ {
        go func(x int) {
            // once.Do() 传入的函数只会执行一次
            once.Do(func() {
                fmt.Printf("只执行一次: %d\n", x)
            })
            // 这行每个 goroutine 都会执行
            fmt.Printf("goroutine %d 完成\n", x)
            done <- true
        }(i)
    }
    
    // 等待所有 goroutine 完成
    for i := 0; i < 10; i++ {
        <-done
    }
}

常见使用场景:

  1. 单例模式:
type Singleton struct{}

var (
    instance *Singleton
    once     sync.Once
)

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{}
    })
    return instance
}
  1. 一次性初始化:
type Config struct {
    settings map[string]string
    once     sync.Once
}

func (c *Config) Load() {
    c.once.Do(func() {
        // 加载配置的代码
        c.settings = make(map[string]string)
        c.settings["key"] = "value"
    })
}
  1. 延迟关闭资源:
type Connection struct {
    once sync.Once
    quit chan struct{}
}

func (c *Connection) Close() {
    c.once.Do(func() {
        close(c.quit)
    })
}
  1. 错误处理:
type ErrorHandler struct {
    once  sync.Once
    err   error
}

func (e *ErrorHandler) HandleError(err error) {
    e.once.Do(func() {
        e.err = err
        // 只处理第一个错误
        fmt.Println("处理错误:", err)
    })
}

sync.Once 的特点:

  1. 线程安全:可以在多个 goroutine 中安全使用
  2. 只执行一次:无论调用多少次,传入的函数只会执行一次
  3. 所有 goroutine 都会等待第一次执行完成
  4. 执行失败不会重试:如果函数执行出错,后续调用也不会重新执行
  5. 不能重置:一旦执行过,就不能重新开始

需要注意的是,sync.Once 的 Do 方法只保证函数执行一次,但不保证是哪个 goroutine 执行的。如果有多个 goroutine 同时调用,只有一个会执行函数,其他的会等待执行完成。