Go并发系列:2基本同步原语-2.5 Once

88 阅读2分钟

2.5 Once

在并发编程中,有时我们需要确保某些初始化操作只执行一次,无论有多少个 goroutine 在同时运行。这类场景在实际开发中非常常见,例如单例模式的初始化、配置文件的加载等。Go 语言中的 sync.Once 提供了一种简洁而高效的方法来实现这一需求。下面我们详细介绍 Once 的概念、使用方法及示例。

2.5.1 什么是Once

Once 是 Go 标准库 sync 包中的一种同步原语,用于确保某些操作只执行一次。无论有多少个 goroutine 同时调用 Do 方法,Once 保证传入的函数只会被执行一次。

Once 主要有一个方法:

  • Do:接收一个无参数、无返回值的函数,确保该函数只执行一次。如果有多个 goroutine 同时调用 Do,只有一个 goroutine 会执行传入的函数,其他 goroutine 会在该函数执行完成后立即返回。

2.5.2 Once的使用方法

使用 Once 非常简单。首先,创建一个 Once 实例。然后,将需要只执行一次的初始化操作封装在一个函数中,并传递给 Do 方法。以下是基本使用示例:

package main

import (
    "fmt"
    "sync"
)

var once sync.Once

func initialize() {
    fmt.Println("Initialization function executed")
}

func main() {
    for i := 0; i < 10; i++ {
        go func(i int) {
            once.Do(initialize)
            fmt.Printf("Goroutine %d finished\n", i)
        }(i)
    }
}

在这个例子中,无论有多少个 goroutine 调用 once.Do(initialize)initialize 函数都只会被执行一次。

2.5.3 Once的应用场景

Once 适用于以下场景:

  1. 单例模式:确保单例对象的创建操作只执行一次。
  2. 全局资源初始化:例如全局配置文件的加载、数据库连接的初始化等。
  3. 延迟初始化:确保某些资源在需要时才被初始化,并且只初始化一次。

2.5.4 示例代码

以下是一个使用 Once 实现单例模式的示例:

package main

import (
    "fmt"
    "sync"
)

type Singleton struct {
    value int
}

var (
    instance *Singleton
    once     sync.Once
)

func getInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{value: 42}
        fmt.Println("Singleton instance created")
    })
    return instance
}

func main() {
    for i := 0; i < 10; i++ {
        go func(i int) {
            s := getInstance()
            fmt.Printf("Goroutine %d: %d\n", i, s.value)
        }(i)
    }

    // Wait for all goroutines to finish
    var wg sync.WaitGroup
    wg.Add(10)
    go func() {
        wg.Wait()
    }()
}

在这个示例中,无论有多少个 goroutine 调用 getInstanceSingleton 实例都只会被创建一次。

结论

Once 提供了一种简单而高效的方法,确保某些初始化操作只执行一次,从而避免了复杂的同步操作和潜在的竞态条件。在实际应用中,合理使用 Once 可以简化并发程序的设计,确保初始化操作的安全性和正确性。在接下来的章节中,我们将继续探讨其他同步原语和并发编程技巧,帮助您更好地掌握 Go 的并发编程。