写点什么

Golang 之不可重入函数实现

  • 2019-11-20
  • 本文字数:1586 字

    阅读完需:约 5 分钟

Golang之不可重入函数实现

一个不可重入的函数就是一个在任何时间点只能执行一次的函数,不管它被调用了多少次,以及有多少 goroutines。


本篇文章说明了阻塞不可重入函数,并在 golang 中产生不可重入的函数实现。

场景用例

某个服务是对某些条件进行轮询,每秒监视一些状态。我们希望每个状态都可以独立地检查,而不需要阻塞。实现可能是这样的:


func main() {    tick := time.Tick(time.Second)    go func() {        for range tick {            go CheckSomeStatus()            go CheckAnotherStatus()        }    }()}
复制代码


我们选择在自己的 goroutine 中运行每个状态检查,以便 CheckAnotherStatus() 不会等待 CheckSomeStatus() 完成。


每一项检查通常都要花费很短的时间,而且比一秒要少得多。但是,如果 CheckAnotherStatus() 本身需要超过一秒的时间运行,会发生什么呢?可能会有一个意外的网络或磁盘延迟影响检查的执行时间。


在同一时间执行两次的函数是否有意义?如果没有,我们希望它是不可重入的。

阻塞,不可重入函数

防止函数多次运行的简单方法是使用 sync.Mutex。


假设我们只关心从上面的循环调用这个函数,我们可以从函数外面实现锁:


import (    "sync"    "time")
func main() { tick := time.Tick(time.Second) var mu sync.Mutex go func() { for range tick { go CheckSomeStatus() go func() { mu.Lock() defer mu.Unlock() CheckAnotherStatus() }() } }()}
复制代码


上面的代码保证了 CheckAnotherStatus() 不是由循环的多次迭代执行的。在以前执行 CheckAnotherStatus() 的时候,循环的任何后续迭代都会被互斥锁阻塞。


阻塞解决方案具有以下属性:


  • 它确保了许多“CheckAnotherStatus()”的调用作为循环迭代的次数。

  • 假设一个执行“CheckAnotherStatus()”的停顿,随后的迭代会导致请求调用相同函数的请求。

屈服,不可重入函数

在我们的状态检查故事中,对随后的 10 个电话堆积起来可能没有意义。一个停滞不前的 CheckAnotherStatus() 执行完成了,所有 10 个调用突然执行,顺序,并且可能在接下来的一秒内完成,在同一秒内完成 10 个相同的检查。


另一个解决办法是屈服。一个有收益的解决方案是:


  • 如果已经执行了“CheckAnotherStatus()”的中止执行。

  • 将最多运行一次“CheckAnotherStatus()”的执行。

  • 与循环迭代的次数相比,实际上可能运行的“CheckAnotherStatus()”的调用更少。


解决方案是通过以下方式实现的:


import (    "sync/atomic"    "time")
func main() { tick := time.Tick(time.Second) var reentranceFlag int64 go func() { for range tick { go CheckSomeStatus() go func() { if atomic.CompareAndSwapInt64(&reentranceFlag, 0, 1) { defer atomic.StoreInt64(&reentranceFlag, 0) } else { return } CheckAnotherStatus() }() } }()}
复制代码


atomic.compareandswapint64(&reentranceFlag, 0, 1) 只有在 reentranceFlag==0 时才会返回 true,并将原子性地设置为 1。在这种情况下,允许进入,并且可以执行该函数。reentranceFlag 保持在 1,直到 CheckAnotherStatus() 完成,此时它被重置。当 CompareAndSwapInt64(…) 返回 false 时,这意味着 reentranceFlag!=0,这意味着该函数已经由另一个 goroutine 执行。代码产生并静默地退出函数。

总结

我们选择在问题的函数之外实现不可重入的代码;我们可以在函数本身中实现它。另外,对于 int64 而言,int32 当然也足够用。以上就是本篇的内容,大家有什么疑问可以在文章下面留言沟通。


本文转载自公众号 360 云计算(ID:hulktalk)。


原文链接:


https://mp.weixin.qq.com/s/6E-tBXc4Sozjr2BkI_0GVA


2019-11-20 16:231187

评论

发布
暂无评论
发现更多内容

ShardingSphere X Google 编程之夏:同学,开源你怎么看?

SphereEx

开源社区 ShardingSphere 谷歌 编程之夏

车云一体的应用价值

SOA开发者

Python代码阅读(第35篇):完全(深度)展开嵌套列表

Felix

Python 编程 Code Programing 阅读代码

再见收费的Navicat!操作所有数据库靠它就够了!

Java 数据库 架构 开源项目

Java 面试的“完美圣经”,有了这些还愁面试吗?

Java 程序员 架构 面试 后端

用21张图,把Git 工作原理彻底说清楚

git 架构 面试 后端

Docgeni 1.1.0 正式发布!

PingCode研发中心

标签 Docgeni 文档目录 进度展示 日志展示

基于HarmonyOS分布式技术,他们让绘画体验更为出色

Geek_283163

鸿蒙

Vue进阶(幺叁幺):父子组件传值实现数据深拷贝

No Silver Bullet

Vue 深拷贝 10月月更

“828页Java面试手册”在我手,何愁offer不到手!

Java 程序员 架构 面试 后端

实时音频抗弱网技术揭秘

百度开发者中心

最佳实践 经验分享 智能视频

为了让你搞定数据库选型,这些工程师重写了 26 万行代码

SphereEx

数据库 架构 架构设计 ShardingSphere SphereEx

MongoDB中文社区 Freetalk,一起来玩快闪!

MongoDB中文社区

mongodb

GitHub标星过万!阿里内部流传的JDK源码剖析手册到底有多强?

程序员 jdk 面试 java

我用 10000 张图片合成我们美好的瞬间

荣顶

JavaScript 大前端 canvas 图形处理

一个约定让全球数万AI爱好者相聚,它是如何做到的?

硬科技星球

2021年9月国产数据库大事记

墨天轮

数据库 华为云 国产数据库 达梦 人大金仓

OpenCV学习(三):三重境界

轻口味

OpenCV图像处理 10月月更

声网 2020 实时大会后的弱网对抗实践

RTE开发者社区

音视频 网络环境 视频编解码 弱网下的极限实时视频通信

The Data Way Vol.5|这里有一场资本与开源的 battle

SphereEx

开源 播客 ShardingSphere SphereEx

10月活动推荐:2021上汽集团“新四化”技术高峰论坛

SOA开发者

[架构实战营]模块九作业

xyu

#架构实战营

每一个用到canvas的小伙伴都应该了解的fabric.js

荣顶

JavaScript 大前端 canvas 图形处理 画布

还在苦恼网络协议?阿里大佬这份笔记带你从入门到精通!

Java 架构 面试 程序人生 编程语言

万字长文,一篇吃透WebSocket:概念、原理、易错常识、动手实践

JackJiang

websocket 即时通讯 IM

想提高运维效率,那就把MySQL数据库部署到Kubernetes 集群中

华为云开发者联盟

MySQL 运维 测试 MySQL数据库 Kubernetes 集群

解读业界5种主流的深度网络模型

华为云开发者联盟

模型 网络模型 模型优化 模型量化 深度网络

AUTOSAR基础篇之OS(上)

SOA开发者

双减来了!人工智能如何促进教育领域转型?

京东科技开发者

人工智能 大数据 AI 教育行业

明道云当选“中国电子商会数据资源服务创新专业委员会”理事单位

明道云

嵌入式软件时序(1)— C语言是怎么编译出来的

SOA开发者

Golang之不可重入函数实现_文化 & 方法_360云计算_InfoQ精选文章