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 25 Golang
golang 实现对Map进行键值自定义排序
Apr 28 Golang
go结构体嵌套的切片数组操作
Apr 28 Golang
解决Golang time.Parse和time.Format的时区问题
Apr 29 Golang
go类型转换及与C的类型转换方式
May 05 Golang
go mod 安装依赖 unkown revision问题的解决方案
May 06 Golang
Golang Gob编码(gob包的使用详解)
May 07 Golang
如何解决goland,idea全局搜索快捷键失效问题
Apr 03 Golang
实现GO语言对数组切片去重
Apr 20 Golang
Golang ort 中的sortInts 方法
Apr 24 Golang
GoFrame基于性能测试得知grpool使用场景
Jun 21 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
《星际争霸II》全新指挥官斯台特曼现已上线
2020/03/08 星际争霸
php下mysql数据库操作类(改自discuz)
2010/07/03 PHP
php导出word格式数据的代码实例
2013/11/25 PHP
PHP rsa加密解密使用方法
2015/04/27 PHP
php封装的验证码工具类完整实例
2016/10/19 PHP
thinkPHP中验证码的简单实现方法
2016/12/05 PHP
PHP实现的下载远程文件类定义与用法示例
2017/07/05 PHP
thinkphp中U方法按路由规则生成url的方法
2018/03/12 PHP
Jquery弹出窗口插件 LeanModal的使用方法
2012/03/10 Javascript
使用JavaScript动态设置样式实现代码及演示动画
2013/01/25 Javascript
js使用html()或text()方法获取设置p标签的显示的值
2014/08/01 Javascript
浅谈Javascript线程及定时机制
2015/07/02 Javascript
浅谈Angular4实现热加载开发旅程
2017/09/08 Javascript
mint-ui在vue中的使用示例
2018/04/05 Javascript
Vue中使用Sortable的示例代码
2018/04/07 Javascript
微信小程序scroll-view横向滑动嵌套for循环的示例代码
2018/09/20 Javascript
[06:20]2015国际邀请赛第三日top10
2015/08/08 DOTA
关于python的bottle框架跨域请求报错问题的处理方法
2017/03/19 Python
Python global全局变量函数详解
2018/09/18 Python
django框架基于模板 生成 excel(xls) 文件操作示例
2019/06/19 Python
pandas数据筛选和csv操作的实现方法
2019/07/02 Python
如何利用Python识别图片中的文字
2020/05/31 Python
HTML5 Canvas绘制五星红旗
2016/05/04 HTML / CSS
斯洛伐克最大的婴儿食品和用品网上商店:Feedo.sk
2020/12/21 全球购物
通信工程专业毕业生推荐信
2013/12/25 职场文书
家居饰品店创业计划书
2014/01/31 职场文书
优秀家长事迹材料
2014/05/17 职场文书
三严三实对照检查材料
2014/09/22 职场文书
出售房屋委托书范本
2014/09/24 职场文书
2016元旦主持人开场白
2015/12/03 职场文书
党员干部学习心得体会
2016/01/23 职场文书
诚信高考倡议书
2019/06/24 职场文书
CocosCreator如何实现划过的位置显示纹理
2021/04/14 Javascript
Go语言中break label与goto label的区别
2021/04/28 Golang
python爬虫之selenium库的安装及使用教程
2021/05/23 Python
python模拟浏览器 使用selenium进入好友QQ空间并留言
2022/04/12 Python