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语言中的UTF-8实现
Apr 26 Golang
golang中的空slice案例
Apr 27 Golang
go语言中GOPATH GOROOT的作用和设置方式
May 05 Golang
golang gopm get -g -v 无法获取第三方库的解决方案
May 05 Golang
Go语言实现Snowflake雪花算法
Jun 08 Golang
golang内置函数len的小技巧
Jul 25 Golang
Go Plugins插件的实现方式
Aug 07 Golang
Go 通过结构struct实现接口interface的问题
Oct 05 Golang
Go语言基础函数基本用法及示例详解
Nov 17 Golang
golang的文件创建及读写操作
Apr 14 Golang
Golang解析JSON对象
Apr 30 Golang
Go调用Rust方法及外部函数接口前置
Jun 14 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实现斐波那契数列的简单写法
2014/07/19 PHP
PHP上传图片类显示缩略图功能
2016/06/30 PHP
PHP读取word文档的方法分析【基于COM组件】
2017/08/01 PHP
PHP项目多语言配置平台实现过程解析
2020/05/18 PHP
给Javascript数组插入一条记录的代码
2007/08/30 Javascript
基于jQuery的简单九宫格实现代码
2012/08/09 Javascript
JS使用for循环遍历Table的所有单元格内容
2014/08/21 Javascript
JQuery实现防止退格键返回的方法
2015/02/12 Javascript
一看就懂:jsonp详解
2015/06/01 Javascript
coffeescript使用的方式汇总
2015/08/05 Javascript
jquery ajax局部加载方法详解(实现代码)
2016/05/12 Javascript
jQuery实现简洁的轮播图效果实例
2016/09/07 Javascript
关于微信上网页图片点击全屏放大效果
2016/12/19 Javascript
javascript中this用法实例详解
2017/04/06 Javascript
Webpack性能优化 DLL 用法详解
2017/08/10 Javascript
node.js的exports、module.exports与ES6的export、export default深入详解
2017/10/26 Javascript
nodejs搭建本地服务器并访问文件操作示例
2019/05/11 NodeJs
微信公众号平台接口开发 菜单管理的实现
2019/08/14 Javascript
微信小程序如何实现在线客服功能
2019/10/16 Javascript
react quill中图片上传由默认转成base64改成上传到服务器的方法
2019/10/30 Javascript
微信小程序实现时间进度条功能
2020/11/17 Javascript
jQuery使用jsonp实现百度搜索的示例代码
2020/07/08 jQuery
nodejs使用Sequelize框架操作数据库的实现
2020/10/21 NodeJs
vue实现标签云效果的示例
2020/11/09 Javascript
原生js实现弹幕效果
2020/11/29 Javascript
python备份文件的脚本
2008/08/11 Python
在Linux系统上通过uWSGI配置Nginx+Python环境的教程
2015/12/25 Python
使用Python的turtle模块画图的方法
2017/11/15 Python
Django框架中序列化和反序列化的例子
2019/08/06 Python
解决安装pyqt5之后无法打开spyder的问题
2019/12/13 Python
CSS3的新特性介绍
2008/10/31 HTML / CSS
个人务虚会发言材料
2014/10/20 职场文书
工作会议通知
2015/04/15 职场文书
商业计划书之服装
2019/09/09 职场文书
MySQL学习之基础操作总结
2022/03/19 MySQL
HTML5页面打开微信小程序功能实现
2022/09/23 HTML / CSS