sync.Once 是 golang里用来实现单例的同步原语。Once 常常用来初始化单例资源,

或者并发访问只需初始化一次的共享资源,或者在测试的时候初始化一次测试资源。

单例,就是某个资源或者对象,只能初始化一次,类似全局唯一的变量。

一般都认为只要使用一个flag标记即可,然后使用原子操作这个flag,代码如下:

type XOnce struct {
done uint32
} func (x *XOnce) Do(f func()) {
if atomic.CompareAndSwapUint32(&x.done, 0, 1) {
f()
}
}

这种方式有很大的问题,就是如果参数f执行很慢,其他调用Do方法的goroutine,

虽然看到done已经设置过值,标记为已执行过,但是初始化资源的函数并未执行完,

在获取初始化资源的时候,可能会得到空的资源或者发生空指针的panic。

来看下go源码中是如何解决这个问题的。

type Once struct {
m sync.Mutex
done uint32
} func (x *Once) Do(f func()) {
if atomic.LoadUint32(&x.done) == 0 {
x.doSlow(f)
}
} func (x *Once) doSlow(f func()) {
x.m.Lock()
defer x.m.Unlock() if x.done == 0 {
defer atomic.StoreUint32(&x.done, 1)
f()
}
}

Once类中有一个互斥锁和一个done标记。

用并发场景来校验一下,假设有两个goroutine同时调用Do方法,并进入doSlow,此时互斥锁的机制保证只有一个g能执行f。

同时利用双检查机制,再次判断x.done是否为,如果是0,则是第一次执行,执行完毕后,将x.done置为1,最后释放锁。

即时第二个g被唤醒了,但是由于此时的x.done==1,也就不会在执行f了。

双检查机制:既保证了并发的goroutine会等待f完成,而且还不会多次执行f

如何实现一个sync.Once的更多相关文章

  1. 在 Linux 上安装配置 BitTorrent Sync [转]

    背景介绍:目前我们线上的前端服务器数量比较多,超过200多台,每次发布新应用的时候,都是将软件包放在一台专门的Push服务器上,再由所有的前端服务器通过rsync自动同步.但随着前端服务器的数量越来越 ...

  2. go语言学习--go的临时对象池--sync.Pool

    一个sync.Pool对象就是一组临时对象的集合.Pool是协程安全的. Pool用于存储那些被分配了但是没有被使用,而未来可能会使用的值,以减小垃圾回收的压力.一个比较好的例子是fmt包,fmt包总 ...

  3. 条件变量 sync.Cond

    sync.Cond 条件变量是基于互斥锁的,它必须有互斥锁的支撑才能发挥作用. sync.Cond 条件变量用来协调想要访问共享资源的那些线程,当共享资源的状态发生变化的时候,它可以用来通知被互斥锁阻 ...

  4. 旧文备份:CANopen中SYNC的功能和使用

    SYNC是CANopen管理各节点同步数据收发的一种方法,相当于网络节拍,基于同步的PDO按照这个网络节拍来执行实时数据的收发.SYNC属于生产/消费型通讯方式,网络中有且只有一个SYNC生产者,一般 ...

  5. go的临时对象池--sync.Pool

    作者:bigtom链接:https://www.jianshu.com/p/2bd41a8f2254來源:简书   一个sync.Pool对象就是一组临时对象的集合.Pool是协程安全的. Pool用 ...

  6. Microsoft Sync Framework下的快速开发同步程序

    Microsoft Sync Frameworks简称MSF,是一个综合的同步平台,MSF支持应用程序,服务,设备的在线以及离线同步.MSF主要有以下几个部件组成:     * Sync Servic ...

  7. sync.Map(在并发环境中使用的map)

    sync.Map 有以下特性: 需要并发读写时,一般的做法是加锁,但这样性能并不高,Go语言在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,sync.Map 和 map 不同,不是 ...

  8. Netty实战:设计一个IM框架

    来源:逅弈逐码 bitchat 是一个基于 Netty 的 IM 即时通讯框架 项目地址:https://github.com/all4you/bitchat 快速开始 bitchat-example ...

  9. Go 互斥锁(sync.Mutex)和 读写锁(sync.RWMutex)

    什么时候需要用到锁? 当程序中就一个线程的时候,是不需要加锁的,但是通常实际的代码不会只是单线程,所以这个时候就需要用到锁了,那么关于锁的使用场景主要涉及到哪些呢? 多个线程在读相同的数据时 多个线程 ...

  10. 【Go】我与sync.Once的爱恨纠缠

    原文链接: https://blog.thinkeridea.com/202101/go/exsync/once.html 官方描述 Once is an object that will perfo ...

随机推荐

  1. 思科交换机BGP配置

    拓扑图后期添加 交换机A配置: Console#show running-configBuilding running configuration. Please wait...!!vlan data ...

  2. 渲染器的实现(1)--《vue.js设计与实现》

    function renderer(domString, container) { container.innerHTML = domString } let count = ref(1) rende ...

  3. Neo4j学习(3)--JavaAPI

    Neo4j Java操作 1. Neo4j Java Driver方式操作 使用该方式对数据进行操作时,必须先将Neo4j的服务启动起来. 从官方下载neo4j的Java驱动:https://neo4 ...

  4. Python学习笔记--数据可视化的开头

    JSON数据格式的转换 示例: 若是有中文数据,可以在data后面加上ensure_ascii=False pyecharts模块 网站:https://gallery.pyecharts.org(有 ...

  5. sdut——4541:小志志和小峰峰的日常(取石子博弈模板题 4合1)

    小志志和小峰峰的日常 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 小志志和小峰峰特别喜欢一起讨论一些很好玩的问题.  ...

  6. RHEL8使用NMCLI管理网络

    使用 NMCLI 配置静态以太网连接 要在命令行上配置以太网连接,请使用 nmcli 工具. 例如,以下流程使用以下设置为 enp7s0 设备创建 NetworkManager 连接配置文件: 静态 ...

  7. git 从本地仓库提交至远程仓库 报错:error: failed to push some refs to "xxx"

    **原因**:远程库里面有个文件,但是本地没有这个文件,比如: README.md,完全提交上去会覆盖之前的文件,所以git会提示报错警告! **解决方案**:如果只有 README.md文件,可以使 ...

  8. 使用requests发送post请求登录

    post请求 语法结构 requests.post(url,data=None,json=None) 参数说明 url:需要爬取的网址 data:请求数据 json:json格式的数据 案例:登录小说 ...

  9. java创建线程的方式有几种?

    java中创建线程的方式有多少种,这个问题也是众多纷纭,这个时候更应该参考官方文档(https://docs.oracle.com/javase/8/docs/api/java/lang/Thread ...

  10. [ACM]NEFUOJ-最长上升子序列

    Description 给出长度为n的数组,找出这个数组的最长上升子序列 Input 第一行:输入N,为数组的长度(2=<N<=50000) 第二行:N个值,表示数组中元素的值(109&l ...