GO Sync.Once 使用

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
    }
  2. 一次性初始化:

    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"
        })
    }
  3. 延迟关闭资源:

    type Connection struct {
        once sync.Once
        quit chan struct{}
    }
    
    func (c *Connection) Close() {
        c.once.Do(func() {
            close(c.quit)
        })
    }
  4. 错误处理:

    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 同时调用,只有一个会执行函数,其他的会等待执行完成。

0%