首页 > 后端开发 > Golang > 正文

Golang如何实现单例模式 讲解sync.Once的线程安全用法

P粉602998670
发布: 2025-08-08 14:21:01
原创
457人浏览过

sync.once 是 go 中实现单例最推荐的方式,它通过 do 方法确保初始化函数仅执行一次。使用时定义一个 once 实例和单例变量,在 getinstance 函数中调用 once.do 来初始化对象,保证并发安全、代码简洁且性能良好。常见误区包括传参错误和复用 once 对象,此外还有全局变量、init 函数和加锁等其他单例实现方式,但均不如 sync.once 安全高效。

Golang如何实现单例模式 讲解sync.Once的线程安全用法

单例模式在Go语言中很常见,尤其是在需要确保某个对象只被初始化一次的场景下。实现方式有很多种,但最推荐、最安全的方式是使用标准库中的

sync.Once
登录后复制

Golang如何实现单例模式 讲解sync.Once的线程安全用法

sync.Once 是什么?

sync.Once
登录后复制
是 Go 标准库提供的一个工具结构体,它保证某个函数只会被执行一次,即使在并发环境下也能线程安全地完成初始化工作。它的定义很简单:

Golang如何实现单例模式 讲解sync.Once的线程安全用法
type Once struct {
    // contains filtered or unexported fields
}
登录后复制

它只有一个方法

Do(f func())
登录后复制
,传入的函数
f
登录后复制
只会被执行一次,后续调用都会被忽略。

立即学习go语言免费学习笔记(深入)”;

这正好符合单例模式的需求:实例只创建一次,多次获取都是同一个对象。

Golang如何实现单例模式 讲解sync.Once的线程安全用法

如何用 sync.Once 实现单例?

实现起来非常简单,基本结构如下:

type Singleton struct{}

var instance *Singleton
var once sync.Once

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{}
    })
    return instance
}
登录后复制

在这个例子中,无论多少个 goroutine 同时调用

GetInstance()
登录后复制
once.Do
登录后复制
都能保证内部的匿名函数只执行一次,从而确保
instance
登录后复制
只被初始化一次。

这种方式的优点很明显:

  • 线程安全,不需要自己加锁
  • 代码简洁清晰,逻辑直观
  • 性能好,不会每次都加锁判断

常见误区和注意事项

虽然

sync.Once
登录后复制
很方便,但在使用过程中也有一些容易踩坑的地方:

Qwen
Qwen

阿里巴巴推出的一系列AI大语言模型和多模态模型

Qwen 691
查看详情 Qwen

✅ 必须传入无参数函数

once.Do()
登录后复制
接受的是一个没有参数的函数,所以如果你的初始化过程需要参数,那就要在闭包里处理清楚,不能直接传参进去。

例如这样是可以的:

once.Do(func() {
    instance = NewSingletonWithConfig(cfg)
})
登录后复制

但不要试图把

Do
登录后复制
当成可以传参执行的函数,它不是设计用来做这个的。

❌ 不要重复使用 Once 对象

每个

sync.Once
登录后复制
只应该用于一个初始化动作。如果你在一个结构体里复用了同一个
Once
登录后复制
对象去做多个初始化,可能会导致混乱。

比如下面这种写法就有问题:

var once sync.Once

func InitA() { once.Do(doA) }
func InitB() { once.Do(doB) } // 错误!InitA执行后,once已经触发过了
登录后复制

一旦其中一个初始化完成,另一个就永远不会再执行了。


单例还可以怎么写?为什么推荐 Once?

除了

sync.Once
登录后复制
,还有几种常见的单例写法:

  • 全局变量直接初始化
    比如

    var instance = &Singleton{}
    登录后复制

    这种方式最简单,但不适合需要延迟加载或有复杂初始化逻辑的情况。

  • 使用 init 函数初始化
    利用包级

    init()
    登录后复制
    函数来初始化单例
    优点是自动执行,缺点是控制力差,无法按需加载。

  • 加锁实现(比如 Mutex)
    并发访问时加锁判断是否已初始化
    虽然可行,但每次都要加锁判断,性能不如

    Once
    登录后复制
    干净利落。

相比之下,

sync.Once
登录后复制
的优势在于:

  • 安全性高:官方封装,经过验证
  • 性能好:只在第一次加锁,之后无开销
  • 语义清晰:一看就知道是用来做“只执行一次”的任务

小细节补充

  • once.Do()
    登录后复制
    中如果函数 panic 了,Once 会认为已经执行过,后续调用也不会再执行。
  • 如果你希望即使失败也重试,就不能用 Once,得自己控制状态。
  • Once 本身是可复制的吗?理论上不应该复制,因为其内部状态可能出错。最好作为包级变量或者结构体内嵌使用。

基本上就这些。用

sync.Once
登录后复制
实现单例是 Go 中最推荐的方式,不复杂但确实实用。

以上就是Golang如何实现单例模式 讲解sync.Once的线程安全用法的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号