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 相关文章推荐
goland 清除所有的默认设置操作
Apr 28 Golang
解决golang在import自己的包报错的问题
Apr 29 Golang
golang协程池模拟实现群发邮件功能
May 02 Golang
golang 实现并发求和
May 08 Golang
Go timer如何调度
Jun 09 Golang
golang fmt格式“占位符”的实例用法详解
Jul 04 Golang
Go Plugins插件的实现方式
Aug 07 Golang
Go 语言中 20 个占位符的整理
Oct 16 Golang
Golang ort 中的sortInts 方法
Apr 24 Golang
Golang入门之计时器
May 04 Golang
深入理解 Golang 的字符串
May 04 Golang
在ubuntu下安装go开发环境的全过程
Aug 05 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中的count函数
2016/05/31 PHP
PHP从二维数组得到N层分类树的实现代码
2016/10/11 PHP
JQUERY CHECKBOX全选,取消全选,反选方法三
2008/08/30 Javascript
学习JavaScript的最佳方法分享
2011/10/21 Javascript
js中如何把字符串转化为对象、数组示例代码
2013/07/17 Javascript
jQuery如何取id有.的值一般的方法是取不到的
2014/04/18 Javascript
Express实现前端后端通信上传图片之存储数据库(mysql)傻瓜式教程(一)
2015/12/10 Javascript
jQuery获取浏览器类型和版本号的方法
2016/07/05 Javascript
原生javascript 学习之js变量全面了解
2016/07/14 Javascript
JavaScript实战之带收放动画效果的导航菜单
2016/08/16 Javascript
jQuery实现Table表格隔行变色及高亮显示当前选择行效果示例
2017/02/14 Javascript
nodejs入门教程六:express模块用法示例
2017/04/24 NodeJs
浅谈在vue中使用mint-ui swipe遇到的问题
2018/09/27 Javascript
Node.js操作系统OS模块用法分析
2019/01/04 Javascript
layer.open弹层查看缩略图的原图,自适应大小的实例
2019/09/05 Javascript
小程序跳转到的H5页面再跳转回跳小程序的方法
2020/03/06 Javascript
python实现根据月份和日期得到星座的方法
2015/03/27 Python
Django使用httpresponse返回用户头像实例代码
2018/01/26 Python
python实现汉诺塔算法
2021/03/01 Python
对Python3之进程池与回调函数的实例详解
2019/01/22 Python
解决Python pip 自动更新升级失败的问题
2020/02/21 Python
python3通过udp实现组播数据的发送和接收操作
2020/05/05 Python
python logging.info在终端没输出的解决
2020/05/12 Python
Python3 + Appium + 安卓模拟器实现APP自动化测试并生成测试报告
2021/01/27 Python
巧用CSS3的calc()宽度计算做响应模式布局的方法
2018/03/22 HTML / CSS
HTML5 Canvas标签使用收录
2009/07/07 HTML / CSS
香港太阳眼镜网上商店:SmartBuyGlasses香港
2016/07/22 全球购物
lookfantastic荷兰:在线购买奢华护肤、护发和化妆品
2018/11/27 全球购物
巴西补充剂和维生素购物网站:Natue
2019/06/17 全球购物
毕业生在校学习的自我评价分享
2013/10/08 职场文书
写给保洁员表扬信
2014/01/08 职场文书
大三毕业自我鉴定
2014/01/15 职场文书
产品质量承诺书
2014/03/27 职场文书
社区党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
派出所班子党的群众路线对照检查材料思想汇报
2014/10/01 职场文书
简单聊聊TypeScript只读修饰符
2022/04/06 Javascript