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 相关文章推荐
Golang 实现超大文件读取的两种方法
Apr 27 Golang
go结构体嵌套的切片数组操作
Apr 28 Golang
golang import自定义包方式
Apr 29 Golang
golang goroutine顺序输出方式
Apr 29 Golang
golang elasticsearch Client的使用详解
May 05 Golang
解决golang 关于全局变量的坑
May 06 Golang
go xorm框架的使用
May 22 Golang
golang中字符串MD5生成方式总结
Jul 04 Golang
修改并编译golang源码的操作步骤
Jul 25 Golang
深入理解go slice结构
Sep 15 Golang
如何利用golang运用mysql数据库
Mar 13 Golang
Golang 并发编程 SingleFlight模式
Apr 26 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&&mysql)四
2006/10/09 PHP
php 随机排序广告的实现代码
2011/05/09 PHP
php数组函数序列之next() - 移动数组内部指针到下一个元素的位置,并返回该元素值
2011/10/31 PHP
php5.5新数组函数array_column使用
2013/07/08 PHP
PHP中常用的转义函数
2014/02/28 PHP
在Linux系统下一键重新安装WordPress的脚本示例
2015/06/30 PHP
PHP中for循环与foreach的区别
2017/03/06 PHP
thinkPHP5.1框架使用SemanticUI实现分页功能示例
2019/08/03 PHP
前端开发部分总结[兼容性、DOM操作、跨域等](持续更新)
2010/03/04 Javascript
javascript 词法作用域和闭包分析说明
2010/08/12 Javascript
JS获取后台Cookies值的小例子
2013/03/04 Javascript
jquery实现效果比较好的table选中行颜色
2014/03/25 Javascript
node.js中的fs.fstat方法使用说明
2014/12/15 Javascript
JQuery选择器绑定事件及修改内容的方法
2015/01/23 Javascript
jQuery使用$.ajax提交表单完整实例
2015/12/11 Javascript
实例详解ECMAScript5中新增的Array方法
2016/04/05 Javascript
利用vue-router实现二级菜单内容转换
2016/11/30 Javascript
用v-html解决Vue.js渲染中html标签不被解析的问题
2016/12/14 Javascript
基于JQuery及AJAX实现名人名言随机生成器
2017/02/10 Javascript
JavaScript中使用参数个数实现重载功能
2017/09/01 Javascript
vue解决跨域路由冲突问题思路解析
2017/11/03 Javascript
Node.js中sequelize时区的配置方法
2017/12/10 Javascript
微信小程序实现复选框效果
2018/12/28 Javascript
记录vue做微信自定义分享的一些问题
2019/09/12 Javascript
JS合并两个数组的3种方法详解
2019/10/24 Javascript
九步学会Python装饰器
2015/05/09 Python
Python中optparser库用法实例详解
2018/01/26 Python
Python django框架应用中实现获取访问者ip地址示例
2019/05/17 Python
python 实现turtle画图并导出图片格式的文件
2019/12/07 Python
python如何判断IP地址合法性
2020/04/05 Python
Hotter Shoes美国官网:英国最受欢迎的舒适鞋
2018/08/02 全球购物
室内设计专业个人的自我评价
2013/10/19 职场文书
研究生考核个人自我鉴定
2014/03/27 职场文书
公证委托书
2014/08/01 职场文书
毕业晚宴祝酒词
2015/08/11 职场文书
Windows Server 2012 修改远程默认端口3389的方法
2022/04/28 Servers