Go语言并发编程 sync.Once


Posted in Golang onOctober 16, 2021

sync.Once用于保证某个动作只被执行一次,可用于单例模式中,比如初始化配置。我们知道init()函数也只会执行一次,不过它是在main()函数之前执行,如果想要在代码执行过程中只运行某个动作一次,可以使用sync.Once,下面来介绍一下它的使用方法。

先来看下面的代码:

package main

import (
 "fmt"
 "sync"
)


func main() {
 var num = 6
 var once sync.Once

 add_one := func() {
  num = num + 1
 }

 minus_one := func() {
  num = num - 1
 } 

 once.Do(add_one)
 fmt.Printf("The num: %d\n", num)
 once.Do(minus_one)
 fmt.Printf("The num: %d\n", num)
}

执行结果:

The num: 7
The num: 7

sync.Once类型提供了一个Do方法,Do方法只接受一个参数,且参数类型必须是func() ,也就是没有参数声明和结果声明的函数。

Do方法只会执行首次被调用时传入的那个函数,只执行一次,也不会执行其它函数。上面的例子中,即使传入的函数不同,也只会执行第一次传入的那个函数。如果有多个只执行一次的函数,需要为每一个函数分配一个sync.Once类型的值:

func main() {
 var num = 6
 var once1 sync.Once
 var once2 sync.Once

 add_one := func() {
  num = num + 1
 }

 minus_one := func() {
  num = num - 1
 } 

 once1.Do(add_one)
 fmt.Printf("The num: %d\n", num)
 once2.Do(minus_one)
 fmt.Printf("The num: %d\n", num)
}

sync.Once类型是一个结构体类型,一个是名为doneuint32类型字段,还有一个互斥锁m

type Once struct {
 done uint32
 m    Mutex
}

done字段的值只可能是0或者1,Do方法首次调用完成后,done的值就变为了1。done的值使用四个字节的uint32类型的原因是为了保证对它的操作是“原子操作”,通过调用atomic.LoadUint32函数获取它的值,如果为1,直接返回,不会执行函数。

如果为0,Do方法会立即锁定字段m,如果这里不加锁,多个goroutine 同时执行到Do方法时判断都为0,则都会执行函数,所以Once是并发安全的。

加锁之后,会再次检查done字段的值,如果满足条件,执行传入的函数,并用原子操作函数atomic.StoreUint32done的值设置为1。

下面是Once的源码:

func (o *Once) Do(f func()) {

 if atomic.LoadUint32(&o.done) == 0 {
  o.doSlow(f)
 }
}

func (o *Once) doSlow(f func()) {
 o.m.Lock()
 defer o.m.Unlock()
 if o.done == 0 {
  defer atomic.StoreUint32(&o.done, 1)
  f()
 }
}

源码非常简洁,和GoF 设计模式中的单例模式非常相似。

到此这篇关于Go语言并发编程 sync.Once的文章就介绍到这了,更多相关Go语言 sync.Once内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Golang 相关文章推荐
win10下go mod配置方式
Apr 25 Golang
go 原生http web 服务跨域restful api的写法介绍
Apr 27 Golang
golang通过递归遍历生成树状结构的操作
Apr 28 Golang
golang elasticsearch Client的使用详解
May 05 Golang
golang 实现时间戳和时间的转化
May 07 Golang
go goroutine 怎样进行错误处理
Jul 16 Golang
Go语言应该什么情况使用指针
Jul 25 Golang
victoriaMetrics库布隆过滤器初始化及使用详解
Apr 05 Golang
golang三种设计模式之简单工厂、方法工厂和抽象工厂
Apr 10 Golang
Golang数据类型和相互转换
Apr 12 Golang
实现GO语言对数组切片去重
Apr 20 Golang
Golang 结构体数据集合
Apr 22 Golang
Go 通过结构struct实现接口interface的问题
Oct 05 #Golang
golang实现一个简单的websocket聊天室功能
深入理解go slice结构
Sep 15 #Golang
Golang表示枚举类型的详细讲解
golang 语言中错误处理机制
Aug 30 #Golang
Golang并发操作中常见的读写锁详析
Aug 30 #Golang
Go中的条件语句Switch示例详解
Aug 23 #Golang
You might like
学习php中的正则表达式
2014/08/17 PHP
PHP实现UTF-8文件BOM自动检测与移除实例
2014/11/05 PHP
php运行时动态创建函数的方法
2015/03/16 PHP
浅谈php处理后端&接口访问超时的解决方法
2016/10/29 PHP
PHP实现的方程求解示例分析
2016/11/11 PHP
PHP判断一个变量是否为整数、正整数的方法示例
2019/09/11 PHP
一个简单的Ext.XTemplate的实例代码
2012/03/18 Javascript
JQuery中根据属性或属性值获得元素(6种情况获取方法)
2013/01/17 Javascript
firefox下jquery ajax返回object XMLDocument处理方法
2014/01/26 Javascript
setInterval计时器不准的问题解决方法
2014/05/08 Javascript
详解JavaScript数组的操作大全
2015/10/19 Javascript
JavaScript函数柯里化详解
2016/04/29 Javascript
详解js中Number()、parseInt()和parseFloat()的区别
2016/12/20 Javascript
详解关于Vue版本不匹配问题(Vue packages version mismatch)
2018/09/17 Javascript
JS实现时间校验的代码
2020/05/25 Javascript
微信小程序canvas动态时钟
2020/10/22 Javascript
详解Python3.1版本带来的核心变化
2015/04/07 Python
python交换两个变量的值方法
2019/01/12 Python
Python实现简单层次聚类算法以及可视化
2019/03/18 Python
Python及Pycharm安装方法图文教程
2019/08/05 Python
简单了解python 生成器 列表推导式 生成器表达式
2019/08/22 Python
Python简易计算器制作方法代码详解
2019/10/31 Python
J2EE的优越性主要表现在哪些方面
2016/03/28 面试题
科室工作个人总结的自我评价
2013/10/29 职场文书
工作表扬信的范文
2014/01/10 职场文书
元旦晚会活动总结
2014/07/09 职场文书
年终晚会活动方案
2014/08/21 职场文书
2014年信访工作总结
2014/11/17 职场文书
运动会广播稿200字
2015/08/19 职场文书
2016元旦文艺汇演主持词(开场白+结束语)
2015/12/03 职场文书
演讲稿之开卷有益
2019/08/07 职场文书
竞选稿之小学班干部
2019/10/31 职场文书
Nginx优化服务之网页压缩的实现方法
2021/03/31 Servers
Python制作一个随机抽奖小工具的实现
2021/07/07 Python
Java字符串逆序方法详情
2022/03/21 Java/Android
css3手动实现pc端横向滚动
2022/06/21 HTML / CSS