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 28 Golang
基于Go Int转string几种方式性能测试
Apr 28 Golang
golang import自定义包方式
Apr 29 Golang
Golang Gob编码(gob包的使用详解)
May 07 Golang
go xorm框架的使用
May 22 Golang
浅谈Golang 切片(slice)扩容机制的原理
Jun 09 Golang
Golang 语言控制并发 Goroutine的方法
Jun 30 Golang
go开发alertmanger实现钉钉报警
Jul 16 Golang
go goroutine 怎样进行错误处理
Jul 16 Golang
go使用Gin框架利用阿里云实现短信验证码功能
Aug 04 Golang
GO语言异常处理分析 err接口及defer延迟
Apr 14 Golang
Go gRPC进阶教程gRPC转换HTTP
Jun 16 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三层结构(上) 简单三层结构
2010/07/04 PHP
JSON两种结构之对象和数组的理解
2016/07/19 PHP
PHP命令Command模式用法实例分析
2018/08/08 PHP
js控制表单操作的常用代码小结
2013/08/15 Javascript
javascript验证上传文件的类型限制必须为某些格式
2013/11/14 Javascript
jQuery判断复选框是否勾选的原理及示例
2014/05/21 Javascript
Javascript学习笔记之函数篇(六) : 作用域与命名空间
2014/11/23 Javascript
javascript中call apply 的应用场景
2015/04/16 Javascript
10个JavaScript中易犯小错误
2016/02/14 Javascript
node.js调用Chrome浏览器打开链接地址的方法
2017/05/17 Javascript
老生常谈js中的MVC
2017/07/25 Javascript
ES6关于Promise的用法详解
2018/05/07 Javascript
vue watch深度监听对象实现数据联动效果
2018/08/16 Javascript
微信小程序实现二维码签到考勤系统
2020/01/16 Javascript
[01:09:23]KG vs TNC 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/16 DOTA
Python选择排序、冒泡排序、合并排序代码实例
2015/04/10 Python
python在线编译器的简单原理及简单实现代码
2018/02/02 Python
python实现C4.5决策树算法
2018/08/29 Python
解决pycharm运行程序出现卡住scanning files to index索引的问题
2019/06/27 Python
让Python脚本暂停执行的几种方法(小结)
2019/07/11 Python
python设计tcp数据包协议类的例子
2019/07/23 Python
利用python计算时间差(返回天数)
2019/09/07 Python
python-docx文件定位读取过程(尝试替换)
2020/02/13 Python
python GUI计算器的实现
2020/10/09 Python
python函数超时自动退出的实操方法
2020/12/28 Python
纯css3实现走马灯效果
2014/12/26 HTML / CSS
Html5插件教程之添加浏览器放大镜效果的商品橱窗
2016/01/07 HTML / CSS
英国哈罗德园艺:Harrod Horticultural
2020/03/31 全球购物
咖啡店自主创业商业计划书
2014/01/22 职场文书
敬老院活动总结
2014/04/28 职场文书
财务助理岗位职责范本
2014/10/09 职场文书
三人合伙协议书范本
2014/10/29 职场文书
清洁工个人总结
2015/03/04 职场文书
2015年仓管员工作总结
2015/04/21 职场文书
2015初中团委工作总结
2015/07/28 职场文书
MySQL数据库Innodb 引擎实现mvcc锁
2022/05/06 MySQL