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 相关文章推荐
go:垃圾回收GC触发条件详解
Apr 24 Golang
Go语言中的UTF-8实现
Apr 26 Golang
golang 定时任务方面time.Sleep和time.Tick的优劣对比分析
May 05 Golang
go 实现简易端口扫描的示例
May 22 Golang
Go timer如何调度
Jun 09 Golang
Go 语言下基于Redis分布式锁的实现方式
Jun 28 Golang
Golang 语言控制并发 Goroutine的方法
Jun 30 Golang
Go 语言结构实例分析
Jul 04 Golang
Go 中的空白标识符下划线
Mar 25 Golang
Golang 遍历二叉树
Apr 19 Golang
Golang jwt身份认证
Apr 20 Golang
Go结合Gin导出Mysql数据到Excel表格
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产生随机字符串函数
2006/12/06 PHP
php执行sql语句的写法
2009/03/10 PHP
晋城吧对DiscuzX进行的前端优化要点
2010/09/05 PHP
使用Smarty 获取当前日期时间和格式化日期时间的方法详解
2013/06/18 PHP
php中过滤非法字符的具体实现
2013/10/29 PHP
PHP解析RSS的方法
2015/03/05 PHP
解读PHP的Yii框架中请求与响应的处理流程
2016/03/17 PHP
javascript之ESC(第二类混淆)
2007/05/06 Javascript
善用事件代理,警惕闭包的性能陷阱。
2011/01/20 Javascript
12款经典的白富美型—jquery图片轮播插件—前端开发必备
2013/01/08 Javascript
javascript 使用正则test( )第一次是 true,第二次是false
2017/02/22 Javascript
基于JavaScript实现焦点图轮播效果
2017/03/27 Javascript
在小程序开发中使用npm的方法
2018/10/17 Javascript
关于ckeditor在bootstrap中modal中弹框无法输入的解决方法
2019/09/11 Javascript
JS实现烟花爆炸效果
2020/03/10 Javascript
python使用新浪微博api上传图片到微博示例
2014/01/10 Python
Python发送email的3种方法
2015/04/28 Python
numpy数组拼接简单示例
2017/12/15 Python
Python利用matplotlib.pyplot绘图时如何设置坐标轴刻度
2018/04/09 Python
Python Selenium 之数据驱动测试的实现
2019/08/01 Python
django 实现将本地图片存入数据库,并能显示在web上的示例
2019/08/07 Python
Python爬虫爬取Bilibili弹幕过程解析
2019/10/10 Python
让IE支持CSS3的不完全兼容方案
2014/09/19 HTML / CSS
HTML+CSS+JavaScript实现图片3D展览的示例代码
2020/10/12 HTML / CSS
努比亚手机官网:nubia
2016/10/06 全球购物
Footshop乌克兰:运动鞋的最大选择
2019/12/01 全球购物
Linux不知道文件后缀名怎么判断文件类型
2012/04/26 面试题
工程师岗位职责
2013/11/08 职场文书
大学生职业生涯规划书汇总
2014/03/20 职场文书
文化活动实施方案
2014/03/28 职场文书
广告学专业毕业生自荐信
2014/05/28 职场文书
运动会广播稿50字
2015/08/19 职场文书
2016抗战胜利71周年红领巾广播稿
2015/12/18 职场文书
《女娲补天》教学反思
2016/02/20 职场文书
学前班教学反思
2016/02/24 职场文书
承诺书应该怎么写?
2019/09/10 职场文书