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语言-为什么返回值为接口类型,却返回结构体
Apr 24 Golang
用golang如何替换某个文件中的字符串
Apr 25 Golang
Go语言切片前或中间插入项与内置copy()函数详解
Apr 27 Golang
使用Golang的channel交叉打印两个数组的操作
Apr 29 Golang
goland 设置project gopath的操作
May 06 Golang
Goland使用Go Modules创建/管理项目的操作
May 06 Golang
Golang实现AES对称加密的过程详解
May 20 Golang
Go遍历struct,map,slice的实现
Jun 13 Golang
go语言使用Casbin实现角色的权限控制
Jun 26 Golang
详解Go语言Slice作为函数参数的使用
Jul 02 Golang
Go语言安装并操作redis的go-redis库
Apr 14 Golang
Go 内联优化让程序员爱不释手
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
PHP新手上路(十一)
2006/10/09 PHP
IIS环境下快速安装、配置和调试PHP5.2.0
2006/12/17 PHP
php array_map()数组函数使用说明
2011/07/12 PHP
php检测图片木马多进制编程实践
2013/04/11 PHP
浅析php原型模式
2014/11/25 PHP
PHP版本的选择5.2.17 5.3.27 5.3.28 5.4 5.5兼容性问题分析
2016/04/04 PHP
用JavaScript玩转游戏物理(一)运动学模拟与粒子系统
2010/06/19 Javascript
JQueryEasyUI datagrid框架的进阶使用
2013/04/08 Javascript
js在输入框屏蔽按键,只能键入数字的示例代码
2014/01/03 Javascript
javascript制作游戏开发碰撞检测的封装代码
2015/03/31 Javascript
简介JavaScript中的getUTCFullYear()方法的使用
2015/06/10 Javascript
jquery实现页面虚拟键盘特效
2015/08/08 Javascript
再谈JavaScript异步编程
2016/01/27 Javascript
快速实现JS图片懒加载(可视区域加载)示例代码
2017/01/04 Javascript
浅谈鸿蒙 JavaScript GUI 技术栈
2020/09/17 Javascript
Python中扩展包的安装方法详解
2017/06/14 Python
django静态文件加载的方法
2018/05/20 Python
Python实现的矩阵转置与矩阵相乘运算示例
2019/03/26 Python
python3获取当前目录的实现方法
2019/07/29 Python
使用tensorboard可视化loss和acc的实例
2020/01/21 Python
python解包概念及实例
2021/02/17 Python
浅谈css3中calc在less编译时被计算的解决办法
2017/12/04 HTML / CSS
html5实现图片转圈的动画效果——让页面动起来
2017/10/16 HTML / CSS
购买澳大利亚最好的服装和内衣在线:BONDS
2016/10/14 全球购物
美国最大的旗帜经销商:Carrot-Top
2018/02/26 全球购物
KELLER SPORTS荷兰:在线订购最好的运动产品
2020/10/13 全球购物
校班主任推荐信范文
2013/12/03 职场文书
《鸟的天堂》教学反思
2014/02/27 职场文书
房地产资料员岗位职责
2014/07/02 职场文书
2014年商场国庆节活动策划方案
2014/09/16 职场文书
学习党的群众路线教育实践活动剖析材料
2014/10/13 职场文书
黑白记忆观后感
2015/06/18 职场文书
观后感的写法
2015/06/19 职场文书
安全生产奖惩制度
2015/08/06 职场文书
车间班组长竞聘书
2015/09/15 职场文书
银行服务理念口号
2015/12/25 职场文书