一、场景

很多时候,我们希望加载配置时只加载一次,因为如连接数据库时较慢且不必加载多次;我们希望关闭通道时只关闭一次,因为关闭多次会报错;我们希望访问一个公共资源时,该资源是完整可靠的。

二、介绍

Golang语言的sync包中的sync.Once可以帮助我们只执行一个场景,其内部只有一个func (o *Once) Do(f func()) { }方法。该方法不能传递参数,所以有这需求时,可以使用闭包解决。

三、问题的引出

下面模仿我们平常不正确的写法,现在有一个场景:有很多个协程要使用到配置资源,一般先判断里面是否为空,如果为空就要去加载配置资源,然后才能继续下面的任务,假设你是这样写的

package mainimport ("fmt""time"
)var con map[string]string // 全局资源func init_ziyuan() { // 加载配置资源con = map[string]string{"hello": "world"}  // 赋值time.Sleep(time.Second) // 模拟耗时操作con = map[string]string{"Areyou": "ok"}  // 赋值
}func panduan(i int) { // 模拟多个协程访问公共资源if con == nil { // 如果配置资源没有加载就需要去加载init_ziyuan()fmt.Println(con)} else {fmt.Println(con, i) // 输出此时公共资源con的值}
}func main() {for i := 0; i < 5; i++ { // 模拟多个协程同时访问公共资源go panduan(i)}time.Sleep(time.Second * 3) // 最笨地方法确保主函数不会提前退出
}
/* 结果
map[hello:world] 4
map[hello:world] 2
map[hello:world] 3
map[Areyou:ok]
map[Areyou:ok]
*/

由结果可知,0、1号协程执行的快,就去执行了加载资源配置,然后执行了配置资源中第一个赋值操作,2、3、4号协程执行的慢,此时0、1号协程已经完成赋值操作,因此con全局资源不为空,所以2、3、4号协程就执行了输出操作,而不执行加载资源配置。

这里就引发了问题:

  • 高并发时,多个协程访问的公共资源不一致

  • 加载配置资源的操作发生了两次甚至可能更多次

如果解决以上问题,我们可能想使用互斥锁对加载配置资源时的全局变量加锁,使得配置没有完成,其他协程就不能访问该资源。

但是,在加锁期间,其他协程由于不能访问公共资源而阻塞,这在高并发情况下是不好的,即影响性能,又由于阻塞而不能释放内存资源,导致内存瞬间爆满

四、sync.Once控制并发安全

在上面代码的基础上只增加了两行代码,如下。

package mainimport ("fmt""sync""time"
)var one sync.Once  // 定义全局结构体
var con map[string]stringfunc init_ziyuan() {con = map[string]string{"hello": "world"}time.Sleep(time.Second)con = map[string]string{"Areyou": "ok"}
}func panduan(i int) {if con == nil {one.Do(init_ziyuan)  // 对资源配置函数调用fmt.Println(con)} else {fmt.Println(con, i)}
}func main() {for i := 0; i < 5; i++ {go panduan(i)}time.Sleep(time.Second * 3)
}
/* 结果
map[hello:world] 2
map[hello:world] 4
map[hello:world] 3
map[hello:world] 0
map[Areyou:ok]
*/

注意:上面的代码在Windows中运行时,结果和错误的代码是一样的,我也不知道为什么。但在Linux环境下执行是对的。可在Golang官网在线执行https://golang.org/

原理(重点)

sync.Once的源码非常简短,也很简单,如下:

// once.go
package syncimport ("sync/atomic"
)type Once struct {done uint32m    Mutex
}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()}
}

done变量是一个没有符号的32位整形,m变量是一个互斥锁。调用Do( init_ziyuan )函数时,首先atomic.LoadUint32( )函数对done进行原子操作,相当于给done加了一个轻量级的锁,并之后返回done这个指针地址指向的值,由于没有初始化,所以它默认为零值,然后执行doSlow( )函数,在doSlow函数中加互斥锁,然后执行f( )函数,也就是我们传进来的init_ziyuan( )函数,执行完后对done进行修改值为1,并设置done的值为1,之后再解锁互斥锁

  • 可以发现done虽然不是一个布尔值,但由于原子操作需要用到int32类型,所以就当做布尔值使用

  • 可以发现互斥锁并不是对全局资源加锁,而是对函数进行加锁,使得函数在短时间内只执行一次

  • 可以发现done变量原子操作是用于保护互斥锁能正常加锁的

Golang高并发安全(一)相关推荐

  1. golang高并发的理解

    转载地址:https://www.cnblogs.com/feixiangmanon/p/10504081.html 前言 GO语言在WEB开发领域中的使用越来越广泛,Hired 发布的<201 ...

  2. Golang的高并发

    Golang的高并发 Golang的调度器有三个核心的元素: 物理处理器 逻辑处理器 goroutine 物理处理器更接近cpu核的概念,主要包括 调度线程来运行. 分配逻辑处理器的基础, 每个物理处 ...

  3. Golang适合高并发场景的原因分析

    典型的两个现实案例: 我们先看两个用Go做消息推送的案例实际处理能力. 360消息推送的数据: 16台机器,标配:24个硬件线程,64GB内存  Linux Kernel 2.6.32 x86_64  ...

  4. Golang适合高并发场景的原理

    典型的两个现实案例: 我们先看两个用Go做消息推送的案例实际处理能力 360消息推送的数据: 16台机器,标配:24个硬件线程,64GB内存  Linux Kernel 2.6.32 x86_64  ...

  5. Golang语言快速上手到综合实战(Go语言、Beego框架、高并发聊天室、豆瓣电影爬虫) 下载

    下载Golang语言快速上手到综合实战(Go语言.Beego框架.高并发聊天室.豆瓣电影爬虫) 下载地址:请加QQ:397245854 Go是Google开发的一种编译型,可并行化,并具有垃圾回收功能 ...

  6. 当Golang遇到高并发秒杀

    遇到go也是工作上的调整,工作上做架构相关的事情,对新发展比较火爆的语言肯定要关注下.就这样步入了GO语言的世界,GO给我带来了全新的体验: 一直做一件事情的人往往会被一件事情所困,开始实践GO语言的 ...

  7. 高并发C/S的TCP版本golang实现

    前面一篇文章写到的实现服务器只能连接一个客户端,没有发挥出go语言的协程特性,所以又可用如下方法实现高并发,多个客户端连接来完成: package mainimport ("fmt" ...

  8. 坑系列 —— 缓存+哈希=高并发?

    2019独角兽企业重金招聘Python工程师标准>>> 今天继续坑系列,高可用已经讲过了,当前互联网时代,怎么少的了高并发呢?高并发和高可用一样, 已经变成各个系统的标配了,如果你的 ...

  9. 小米开源监控系统OpenFalcon应对高并发7种手段

    2019独角兽企业重金招聘Python工程师标准>>> 小米开源监控系统OpenFalcon应对高并发7种手段 原创 2016-04-01 秦晓辉 高可用架构 编者按:本文是秦晓辉在 ...

最新文章

  1. ubuntu系统在vmware中无法联网问题解决
  2. Django模版(二)
  3. 搜索引擎——反向索引原理揭秘及手写ik分词器
  4. linux下如何批量替换多个文件中的某个字符串?
  5. 字符串常量与字符串变量
  6. Cacti Plugin Architecture安装
  7. 复旦大学邱锡鹏教授带你梳理深度学习知识脉络(直播彩蛋)
  8. android6.0 framework修改使用两个声卡
  9. MySQL的索引与事务(面试必考) - 细节狂魔
  10. Rust Atomic总结笔记
  11. Access Token是做什么的
  12. mybatis 一对一、一对多、多对一、多对多
  13. 在线生成安卓签名证书
  14. android换肤动画,Android换肤(二) — 插件式换肤
  15. 寒武纪行歌 - 智能驾驶大算力芯片入局者
  16. DB2数据库常见问题汇总
  17. filter- 配置多过滤url
  18. 2019-6-27-WPF-如何给定两个点画出一条波浪线
  19. 博通Broadcom SDK源码学习与开发11——Cable Modem DHCP管理
  20. 给刚出社会的您关于社会的几点建议与看法

热门文章

  1. python是什么意思中文、好学吗-python好学吗
  2. (读书笔记) 暗时间 (2016.12.17更)
  3. EXCELVBA: 中国热力图 HeatMap of China
  4. 慎读书,精读书,反复读好书并学以致用
  5. CTF之懵懂时期系列---后台登录
  6. H3C防火墙web管理
  7. 核心骨干需要具备的能力
  8. element中el-dialog组件垂直居中完美解决方案,适配超高dialog
  9. 十大战略工具(5)—— 通用电气矩阵
  10. 刺激战场android闪退,《绝地求生刺激战场》老是闪退怎么办 老是闪退解决方法介绍...