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. 什么是js严格模式?

    (use strict)严格模式是一种将更好的错误检查引入代码中的方法. 在使用严格模式时,无法使用隐式声明的变量.将值赋给只读属性或将属性添加到不可扩展的对象等. 1. 严格模式的目的 1) 消除J ...

  2. day2 java基础语法

    day1复习 1.java的特点 2.jdk,jre,jvm的关系 3.为什么要配置path  基本语法 1.关键字与保留字 2.标识符与标识符规则 3.java的命名规范 起名时提高阅读性尽量有意义 ...

  3. UGUI按Tab键切换输入框

    脚本挂在输入框的父物体上即可 [code]csharpcode: using System.Collections; using System.Collections.Generic; using U ...

  4. Nodejs 发送 TCP 消息的正确姿势

    最近使用 NODE-RED 跟 TCP 打交道.NODE-RED 里内建了一个节点叫"tcp-out",看文档呢使用这个节点可以很方便的把 payload 用 TCP 协议发送出去 ...

  5. 个人数据保全计划:(2) NAS基础知识

    前言 距离去年国庆入手了NAS至今有好几个月时间了,NAS折腾起来有点麻烦,且实际作用因人而异,并没有想象中的好用,所以说好的这个系列一直没有更新~ 还有另一方面的原因,这些NAS的系统基于Linux ...

  6. Android笔记--案例:找回密码

    找回密码 具体实现: 登录成功: 报告密码不同: 报告验证码错误: 代码相关: 找回密码的界面很简单,不细说了,直接写就行 找回密码的逻辑实现: 下一次就去写数据存储啦! 拜拜!

  7. Kubecost - Kubernetes 开支监控和管理

    ️URL: https://www.kubecost.com/ Description: Kubeccost 为使用 Kubernetes 的团队提供实时成本可视化和洞察,帮助您持续降低云成本. 昨天 ...

  8. 自己动手从零写桌面操作系统GrapeOS系列教程——20.汇编语言读硬盘实战

    学习操作系统原理最好的方法是自己写一个简单的操作系统. 本讲我们设计一个简单的读硬盘实验.通过一定的方法使硬盘第二个扇区的前3个字节依次为1.2.3,最后3个字节依次为3.2.1,中间的506个字节全 ...

  9. redis 5种数据类型的增删改查

    string: 增:set name zhangsan 删:del name 改:set name lisi 查:get name hash: 增:hmset name name1 zhangsan ...

  10. Oracle_用户-授权-角色

    Oracle创建用户及表空间 1. 用户 创建用户: sql> create user <用户名> IDENTIFIED BY <用户密码> default tables ...