GO学习之 单例模式 sync.Once
GO系列
1、GO学习之Hello World
2、GO学习之入门语法
3、GO学习之切片操作
4、GO学习之 Map 操作
5、GO学习之 结构体 操作
6、GO学习之 通道(Channel)
7、GO学习之 多线程(goroutine)
8、GO学习之 函数(Function)
9、GO学习之 接口(Interface)
10、GO学习之 网络通信(Net/Http)
11、GO学习之 微框架(Gin)
12、GO学习之 数据库(mysql)
13、GO学习之 数据库(Redis)
14、GO学习之 搜索引擎(ElasticSearch)
15、GO学习之 消息队列(Kafka)
16、GO学习之 远程过程调用(RPC)
17、GO学习之 goroutine的调度原理
18、GO学习之 通道(nil Channel妙用)
19、GO学习之 同步操作sync包
20、GO学习之 互斥锁、读写锁该如何取舍
21、GO学习之 条件变量 sync.Cond
22、GO学习之 单例模式 sync.Once
文章目录
- GO系列
- 前言
- sync.Once 如何使用
前言
按照公司目前的任务,go 学习是必经之路了,虽然行业卷,不过技多不压身,依旧努力!!!
那sync.Once
又是什么呢?某个 goroutine 只需要且只能执行一次,到目前为止我们知道程序运行期间只被执行一次的就是每个包的 init
函数,sync包也提供了这样一种更灵活的机制,可以保证 任意一个函数 在程序运行期间只被执行一次,就是 sync.Once了。
sync.Once 如何使用
在 JAVA 开发中,Spring 框架使用了 单例模式 来创建实体 Bean,我们在开发中也是多用单例模式来避免实例的重复创建。
在 Go 标准库中,sync.Once 的 “仅仅执行一次” 的特性被多用于初始化操作或者是资源清理的过程中,以避免重复执行导致性能不是最佳。
sync.Once 的语义十分适合实现单例模式(singleton)模式,并且实现起来也和简单,如下获取实例的例子:
package main
import (
"log"
"sync"
"time"
)
// 定义一个结构体对象
type Entity struct{}
var once sync.Once
// 声明一个接口实例,用于存放实例
var instance *Entity
func GetFun(id int) *Entity {
// 延迟获取异常信息
defer func() {
e := recover()
if e != nil {
log.Printf("goroutine %d catch a panic: %s \n", id, e)
}
}()
log.Printf("goroutine %d start run... \n", id)
// once.Do 真正的执行创建实体操作
once.Do(func() {
instance = &Entity{}
time.Sleep(3 * time.Second)
log.Printf("goroutine %d create instance : %p\n", id, instance)
panic("success to create a instance")
})
return instance
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
inst := GetFun(i)
log.Printf("goroutine %d get a instance: %p", i, inst)
wg.Done()
}(i + 1)
}
time.Sleep(5 * time.Second)
inst := GetFun(0)
log.Printf("goroutine %d get a instance: %p", 0, inst)
wg.Wait()
log.Printf("all goroutine done \n")
}
运行结果:
PS D:\workspaceGo\src\sync> go run .\sync_once_main.go
2023/12/02 16:17:04 goroutine 5 start run...
2023/12/02 16:17:04 goroutine 2 start run...
2023/12/02 16:17:04 goroutine 1 start run...
2023/12/02 16:17:04 goroutine 3 start run...
2023/12/02 16:17:04 goroutine 4 start run...
2023/12/02 16:17:07 goroutine 5 create instance : 0xa477c0
2023/12/02 16:17:07 goroutine 5 catch a panic: success to create a instance
2023/12/02 16:17:07 goroutine 5 get a instance: 0x0
2023/12/02 16:17:07 goroutine 3 get a instance: 0xa477c0
2023/12/02 16:17:07 goroutine 2 get a instance: 0xa477c0
2023/12/02 16:17:07 goroutine 4 get a instance: 0xa477c0
2023/12/02 16:17:07 goroutine 1 get a instance: 0xa477c0
2023/12/02 16:17:09 goroutine 0 start run...
2023/12/02 16:17:09 goroutine 0 get a instance: 0xa477c0
2023/12/02 16:17:09 all goroutine done
从运行结果中我们可以观察到,sync.Do 会等待 func() 执行完毕才返回,在这期间其他的执行 once.Do 函数的 goroutine(如上述例子的 goroutine 1 ~ 4) 将会阻塞等待。
等 goroutine 5 的 sync.Do 执行完后,其他的 goroutine 在调用 sync.Do 时将不再执行里面的 func() 并立即返回(如 goroutine 0)。
我们还可以观察到,即便在函数 func() 中出现 panic, sync.Once 依旧认为 once.Do 执行完毕,其他 goroutine 调用 once.Do 将不再执行 func() 。
现阶段还是对 Go 语言的学习阶段,想必有一些地方考虑的不全面,本文示例全部是亲自手敲代码并且执行通过。
如有问题,还请指教。
评论去告诉我哦!!!一起学习一起进步!!!