NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

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:23770

评论

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

你不知道的开源分布式存储系统 Alluxio 源码完整解析(上篇)

腾源会

大数据 开源 数据湖

范学雷的专栏《深入剖析 Java 新特性》

IT蜗壳-Tango

11月日更

CNCF 沙箱再添“新将”!云原生边缘容器开源项目 SuperEdge 正式入选

腾源会

开源 容器 云原生 cncf

识别AI换脸!百度这项技术夺冠了!

百度大脑

人工智能 百度

Ubuntu系统下《汇编语言》环境配置

codists

汇编语言

【算法技术专题】如何用Java实现一致性 hash 算法( consistent hashing )(上)

洛神灬殇

算法 一致性hash 11月日更

jodconverter实现在线预览

小鲍侃java

11月日更

监管打压加码!虚拟货币挖矿再遭围堵 “漏网之鱼”当休

CECBC

架构设计

AHUI

「架构实战营」

一起听、一起看、一起唱掀起Z世代青年社交浪潮

声网

人工智能 算法 音视频

你不知道的开源分布式存储系统 Alluxio 源码完整解析(下篇)

腾源会

大数据 开源

Android C++系列:JNI操作Bitmap

轻口味

c++ android jni 11月日更

一文告诉你 K8s PR (Pull Request) 怎样才能被 merge?

腾源会

k8s

NodeJs 深入浅出之旅:V8 内存分配🧙‍♂️

空城机

大前端 Node 11月日更

linux下清理系统缓存并释放内存

入门小站

Linux

在线假单词随机生成器

入门小站

工具

中央银行、不平等和新技术:使用分布式账本、可编程合约和密码学的蓝图

CECBC

15 K8S之容器安全上下文

穿过生命散发芬芳

k8s 11月日更

1 分钟学会 30 种编程语言

AlwaysBeta

新能源汽车补贴没了,行业还能快速发展吗?

石云升

学习笔记 新能源汽车 11月日更

一个基于PoS共识算法的区块链案例

Regan Yue

区块链 共识算法 11月日更 细讲区块链

【LeetCode】重新排序得到 2 的幂Java题解

Albert

算法 LeetCode 11月日更

数据同步:教你如何实时把数据从 MySQL 同步到 OceanBase

OceanBase 数据库

数据库 开源 oceanbase 分布式,

科技热点周刊|马斯克套现 440 亿;苹果推出数字身份证;Meta 与微软合作;华为捐赠欧拉

青云技术社区

云计算 物联网

浏览器的几种防护策略

网络安全学海

网络安全 信息安全 渗透测试 WEB安全 安全漏洞

这一次,Google 终于对 Web 自动化下手了!

星安果

chrome 自动化

在华为云专属月,找到开启互联网第二增长曲线的一把钥匙

脑极体

赢在2022,面试官常问的软件测试面试题总结

六十七点五

软件测试 面试题 自动化测试 经验总结 测试工程师

请问软件测试和渗透测试的区别是什么?

喀拉峻

网络安全 渗透测试

验证码

卢卡多多

图片验证码 11月日更

你现在可以在元宇宙里 “打工”了!

CECBC

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