news 2026/2/28 9:16:28

go sync.oncevalue一个单例的更简实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
go sync.oncevalue一个单例的更简实现

我来为你详细解析 `sync.OnceValue` —— Go 1.21 引入的并发安全初始化工具。

核心概念

`sync.OnceValue` 是 Go 1.21 新增的泛型函数,用于惰性且线程安全地初始化一个值:

```go
func OnceValue[T any](f func() T) func() T
```

它返回一个闭包,确保 `f` 只执行一次,后续调用直接返回缓存结果。

---

基础用法

```go
package main

import (
"fmt"
"sync"
"time"
)

func main() {
// 定义一个昂贵的初始化操作
initConfig := sync.OnceValue(func() map[string]string {
fmt.Println("⚙️ 正在加载配置... (只执行一次)")
time.Sleep(100 * time.Millisecond) // 模拟耗时操作

return map[string]string{
"db_host": "localhost",
"db_port": "5432",
"api_key": "secret-123",
}
})

// 并发多次获取配置
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()

config := initConfig() // 并发安全调用
fmt.Printf("Goroutine %d: %v\n", id, config["db_host"])
}(i)
}

wg.Wait()

// 再次调用,直接返回缓存值
cfg := initConfig()
fmt.Printf("Final call: %v\n", cfg)
}
```

输出:

```
⚙️ 正在加载配置... (只执行一次)
Goroutine 2: localhost
Goroutine 0: localhost
Goroutine 4: localhost
Goroutine 1: localhost
Goroutine 3: localhost
Final call: map[api_key:secret-123 db_host:localhost db_port:5432]
```

---

与 `sync.Once` 的对比

特性 `sync.Once` `sync.OnceValue`
代码量 需要额外变量存储结果 自动缓存返回值
类型安全 需手动类型断言 泛型支持,类型安全
复用性 封装较繁琐 返回闭包,可传递
错误处理 需手动处理 需配合 `OnceValue` + `error` 模式

传统 `sync.Once` 写法

```go
var (
once sync.Once
config map[string]string
)

func GetConfig() map[string]string {
once.Do(func() {
config = loadConfig() // 需外部变量存储
})
return config
}
```

`OnceValue` 写法(更简洁)

```go
var getConfig = sync.OnceValue(func() map[string]string {
return loadConfig()
})

// 使用:config := getConfig()
```

---

高级场景

1. 带错误处理的初始化(`OnceValues`)

```go
// Go 1.21 也提供了 OnceValues 用于多返回值
var initDB = sync.OnceValues(func() (*sql.DB, error) {
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, err
}
return db, db.Ping()
})

// 使用
db, err := initDB()
```

2. 单例模式实现

```go
type Service struct {
client *http.Client
}

var getService = sync.OnceValue(func() *Service {
return &Service{
client: &http.Client{Timeout: 30 * time.Second},
}
})

func Instance() *Service {
return getService()
}
```

3. 延迟计算 + 缓存

```go
// 计算斐波那契数列(带缓存)
var fib = sync.OnceValue(func() []int {
fmt.Println("计算斐波那契数列...")
seq := make([]int, 50)
seq[0], seq[1] = 0, 1
for i := 2; i < 50; i++ {
seq[i] = seq[i-1] + seq[i-2]
}
return seq
})

// 多次调用,只计算一次
fmt.Println(fib()[10]) // 55
fmt.Println(fib()[20]) // 6765
```

---

关键特性总结

1. 线程安全:内部使用 `sync.Once`,保证并发安全
2. 惰性求值:首次调用时才执行初始化
3. 结果缓存:执行结果永久缓存,后续调用零开销
4. 泛型支持:编译期类型检查,无需类型断言
5. 不可重置:一旦初始化完成,无法重新执行(如需重置需重新创建)

---

适用场景

- ✅ 全局配置加载
- ✅ 数据库连接池初始化
- ✅ 单例服务对象
- ✅ 昂贵的计算结果缓存
- ✅ 资源预加载(图片、模板等)

`sync.OnceValue` 让 Go 的惰性初始化代码更加简洁、类型安全,是替代传统 `sync.Once` + 全局变量的现代最佳实践。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/27 6:38:38

程序员为自己的工具命名时的彻底迷失【翻译】

英文&#xff1a;https://larr.net/p/namings.html翻译&#xff1a; 程序员和软件开发者在命名工具时迷失了方向 引言 2022年12月&#xff0c;我观看了理查德斯托曼在EmacsConf上的演讲&#xff0c;题目是"我希望在Emacs中看到什么"。斯托曼先生在这次演讲中提到的一…

作者头像 李华
网站建设 2026/2/25 8:26:13

基于STM32F103驱动QMI8658A输出加速度陀螺仪数据

基于STM32F103驱动QMI8658A输出加速度陀螺仪数据 简介QMI8658A和QMI8658C区别QMI8658A引脚定义QMI8658A寄存器表代码驱动接线代码 现象总结 简介 QMI8658A 是上海矽睿&#xff08;QST&#xff09;推出的一款高性能 6 轴惯性测量单元&#xff08;IMU&#xff09;芯片&#xff0…

作者头像 李华
网站建设 2026/2/19 13:25:04

大数据计算机毕设之基于python+Hadoop的国家气象降雨量大数据分析系统基于大数据的多维度气象数据的可视化分析系统(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/2/24 6:01:21

高校物业维修管理微信小程序的设计和实现

&#x1f497;博主介绍&#xff1a;✌全网粉丝20W,CSDN全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云等平台优质作者,计算机毕设实战导师。目前专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌ &#x1f497;主要服务内…

作者头像 李华
网站建设 2026/2/24 20:32:07

【开题答辩全过程】以 基于Springboot个人健康运动系统的设计与实现为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

作者头像 李华