当前位置: 首页 > news >正文

【从零单排Golang】第十五话:用sync.Once实现懒加载的用法和坑点

在使用Golang做后端开发的工程中,我们通常需要声明一些一些配置类或服务单例等在业务逻辑层面较为底层的实例。为了节省内存或是冷启动开销,我们通常采用lazy-load懒加载的方式去初始化这些实例。初始化单例这个行为是一个非常经典的并发处理的案例,比如在java当中,我们可能用到建立双重锁+volatile的方式保证初始化逻辑只被访问一次,并且所有线程最终都可以读取到初始化完成的实例产物。这段经典的代码可以按如下的方式编写:

// 参考:https://blog.csdn.net/qq_27489007/article/details/84966680public class Singleton {private volatile static Singleton uniqueSingleton;private Singleton() {}public Singleton getInstance() {if (null == uniqueSingleton) {synchronized (Singleton.class) {if (null == uniqueSingleton) {uniqueSingleton = new Singleton();}}}return uniqueSingleton;}
}

但在Golang里面,实现懒加载的方式可以简单的多,用内置的sync.Once就能满足。假设我们有一个user单例,需要被1000个线程读取并打印,就可以这样子写:

type User struct {Name string `json:"name"`Age  int    `json:"age"`
}var user *User
var userOnce sync.Oncefunc initUser() {user = &User{}cfgStr := `{"name":"foobar","age":18}`if err := json.Unmarshal([]byte(cfgStr), user); err != nil {panic("load user err: " + err.Error())}
}func getUser() *User {userOnce.Do(initUser)return user
}func TestSyncOnce(t *testing.T) {var wg sync.WaitGroupfor i := 1; i < 1000; i++ {wg.Add(1)go func(n int) {defer wg.Done()curUser := getUser()t.Logf("[%d] got user: %+v", n, curUser)}(i)}wg.Wait()
}

这段代码里,首先是通过var userOnce sync.Once声明了一个sync.Once实例,然后在getUser当中,我们声明了userOnce.Do(initUser)这个操作。假设一个goroutine最先到达这个操作,就会上锁并执行initUser,其它goroutine到达之后,得等第一个goroutine执行完initUser之后,才会继续return user。这样,就能一来保证initUser只会执行一次,二来所有goroutine都能够最终读到初始化完成的user单例。

sync.Once的工作机理也很简单,通过一个锁和一个flag就能够实现:

func (o *Once) Do(f func()) {if atomic.LoadUint32(&o.done) == 0 { // 如果是1表示已经完成了,跳过o.doSlow(f)}
}func (o *Once) doSlow(f func()) {o.m.Lock() // 只有1个goroutine能拿到锁,其它的等待defer o.m.Unlock()if o.done == 0 { // 如果还是0表示第一个来的,不是0就表示已经有goroutine做完了defer atomic.StoreUint32(&o.done, 1)f()}
}

最后也需要注意,sync.Once使用上面有一个坑点,不能也不需要像java一样为单例提前做nil判断。比如下面一段代码是有问题的:

func initUser() {user = &User{} // 先给一个zero-value实例cfgStr := `{"name":"foobar","age":18}` // 然后加载json内容,完成初始化if err := json.Unmarshal([]byte(cfgStr), user); err != nil {panic("load user err: " + err.Error())}
}func getUser() *User {if user == nil {userOnce.Do(initUser)}return user
}

由于Golang没有volatile关键字,不能控制单例在内存的可见性,那么多goroutine并发时,就有可能出现这样的执行时序:

  • goroutine-A过了getUseruser == nil判断,进入到了initUser逻辑,走到了cfgStr := XXX一行
  • 此时切换到goroutine-B,因为goroutine-AinitUser已经走过了user = &User{}一行,所以跳过了user == nil判断,直接返回没有完全初始化的user实例,然后一直往下运行,就没切回给goroutine-A

这样的结果,就导致有goroutine拿到未初始化完成的实例往后运行,后面就出问题了。所以实战当中需要留意,用sync.Once时,不能也不需要加这些nil判断,就能满足懒加载单例/配置之类的逻辑。

相关文章:

【从零单排Golang】第十五话:用sync.Once实现懒加载的用法和坑点

在使用Golang做后端开发的工程中&#xff0c;我们通常需要声明一些一些配置类或服务单例等在业务逻辑层面较为底层的实例。为了节省内存或是冷启动开销&#xff0c;我们通常采用lazy-load懒加载的方式去初始化这些实例。初始化单例这个行为是一个非常经典的并发处理的案例&…...

常见注意力机制

注意力机制 &#xff08;具有自适应性&#xff09; 18年提出的一种新的 卷积注意力模块 &#xff1b;对前馈卷积神经网络 是一个 简单而有效的 注意力模块 &#xff1b; 因为它的 轻量级和通用性 &#xff0c;可以 无缝集成到任何CNN网络 当中&#xff0c; 对我们来讲&…...

解决报错之org.aspectj.lang不存在

一、IDEA在使用时&#xff0c;可能会遇到maven依赖包明明存在&#xff0c;但是build或者启动时&#xff0c;报找不存在。 解决办法&#xff1a;第一时间检查Setting->Maven-Runner红圈中的√有没有选上。 二、有时候&#xff0c;明明依赖包存在&#xff0c;但是Maven页签中…...

java之SpringBoot基础篇、前后端项目、MyBatisPlus、MySQL、vue、elementUi

文章目录 前言JC-1.快速上手SpringBootJC-1-1.SpringBoot入门程序制作&#xff08;一&#xff09;JC-1-2.SpringBoot入门程序制作&#xff08;二&#xff09;JC-1-3.SpringBoot入门程序制作&#xff08;三&#xff09;JC-1-4.SpringBoot入门程序制作&#xff08;四&#xff09;…...

golang中如何判断字符串是否包含另一字符串

golang中如何判断字符串是否包含另一字符串 在Go语言中&#xff0c;可以使用strings.Contains()函数来判断一个字符串是否包含另一个字符串。该函数接受两个参数&#xff1a;要搜索的字符串和要查找的子字符串&#xff0c;如果子字符串存在于要搜索的字符串中&#xff0c;则返…...

ONNX OpenVino TensorRT MediaPipe NCNN Diffusers ComfyUI

框架 和Java生成的中间文件可以在JVM上运行一样&#xff0c;AI技术在具体落地应用方面&#xff0c;和其他软件技术一样&#xff0c;也需要具体的部署和实施的。既然要做部署&#xff0c;那就会有不同平台设备上的各种不同的部署方法和相关的部署架构工具 onnx 在训练模型时可以…...

java中使用 Integer 和 int 的 含义、使用方法 及之间的区别

学习目标&#xff1a; 学习目标如下&#xff1a; 明确 Integer 和 int 的 含义、使用方法 及之间的区别 学习内容&#xff1a; 一、区别&#xff1a; 1.Integer是int的包装类&#xff0c;int则是java的一种基本的数据类型&#xff1b; 2.Integer变量必须实例化之后才能使用&a…...

点云从入门到精通技术详解100篇-点云的特征检测

目录 前言 点云配准的研究背景 多元时间序列的相似性分析研究背景及意义 国内外研究现状...

DOM破坏绕过XSSfilter例题

目录 一、什么是DOM破坏 二、例题1 ​编辑 三、多层关系 1.Collection集合方式 2.标签关系 四、例题2 一、什么是DOM破坏 DOM破坏&#xff08;DOM Clobbering&#xff09;指的是对网页上的DOM结构进行不当的修改&#xff0c;导致页面行为异常、性能问题、安全风险或其他不…...

代码随想录Day_56打卡

①、两个字符串的删除操作 给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 事例&#xff1a; 输入: word1 "sea", word2 "eat" 输出: 2 解释: 第一步将 "sea&…...

高忆管理:六连板捷荣技术或难扛“华为概念股”大旗

在本钱商场上名不见经传的捷荣技术&#xff08;002855.SZ&#xff09;正扛起“华为概念股”大旗。 9月6日&#xff0c;捷荣技术已拿下第六个连续涨停板&#xff0c;短短七个生意日&#xff0c;股价累积涨幅逾越90%。公司已连发两份股票生意异动公告。 是炒作&#xff0c;还是…...

「解析」YOLOv5 classify分类模板

学习深度学习有些时间了&#xff0c;相信很多小伙伴都已经接触 图像分类、目标检测甚至图像分割(语义分割)等算法了&#xff0c;相信大部分小伙伴都是从分类入门&#xff0c;接触各式各样的 Backbone算法开启自己的炼丹之路。 但是炼丹并非全是 Backbone&#xff0c;更多的是各…...

交换排序——冒泡排序、快速排序

交换排序就是通过比较交换实现排序。分冒泡排序和快速排序两种。 一、冒泡排序&#xff1a; 1、简述 顾名思义就是大的就冒头&#xff0c;换位置。 通过多次重复比较、交换相邻记录而实现排序&#xff1b;每一趟的效果都是将当前键值最大的记录换到最后。 冒泡排序算法的原…...

Android 10.0 禁用adb shell input输入功能

1.前言 在10.0的产品开发中,在进行一些定制开发中,对于一些adb shell功能需要通过属性来控制禁止使用input 等输入功能,比如adb shell input keyevent 响应输入事件等,所以就需要 熟悉adb shell input的输入事件流程,然后来禁用adb shell input的输入事件功能,接下来分…...

cuda显存访问耗时

背景&#xff1a; 项目中有个数据量大小为5195 * 512 * 128float 1.268G的显存&#xff0c;发现有个函数调用很耗时&#xff0c;函数里面就是对这个显存进行128个元素求和&#xff0c;得到一个5195 * 512的图像 分析 1. 为什么耗时 直观上感觉这个流程应该不怎么耗时才对&a…...

【HTML5高级第三篇】drag拖拽、音频视频、defer/async属性、dialog应用

文章目录 一、拖拽事件1.1 拖拽事件1.2 案例&#xff1a;拖拽丢弃图片 二、音频和视频三、defer 与 async 属性3.1 概述3.2 示例一&#xff1a;3.3 示例二&#xff1a; 四、dialog 元素 一、拖拽事件 原生JavaScipt案例合集 JavaScript DOM基础 JavaScript 基础到高级 Canvas…...

独享IP vs. 共享IP:哪种更适合你?

无论是个人用户还是企业组织&#xff0c;在互联网上都需要一个唯一标识来与其他设备进行通信。这就涉及到使用独立分配给自己或多个用户分享的公共 IP 地址&#xff08;也称为共享 IP&#xff09;。那么&#xff0c;究竟应该选择独占一个专用地址还是与他人分享相同地址呢&…...

【Arduino27】DHT11温湿度传感器模拟值实验

硬件准备 DHT11温湿度&#xff1a;1个 面包板&#xff1a;1个 杜邦线&#xff1a;3根 硬件连线 VDD引脚接 5V 电源 DATE引脚接 4号 接口 GND引脚接 GND 接口 软件程序 #include<DHT.h>#define DHT11_pin 4 //温湿度传感器引脚DHT dht(DHT11_pin,DHT11);float tem…...

dockerfile基于apline将JDK20打包成镜像

dockerfile基于apline将JDK20打包成镜像 ​ 今天就来和大家聊聊如何把最新出版的JDK20打包成docker镜像&#xff0c;很多uu都会采用centos作为基础镜像&#xff0c;这么做会有一个问题&#xff0c;centos系统会含有很多库文件&#xff0c;这些库文件JDK程序并不是完全需要的&a…...

MATLAB基础-MAT文件的读写操作

简介 MAT文件是MATLAB格式的双精度二进制数据文件&#xff0c;由MATLAB软件创建&#xff0c;可以使用MATLAB软件再其他计算机上以其他浮点格式读取&#xff0c;同时也可以使用其他软件通过MATLAB的应用程序接口来进行读写操作。如果只是再MATLAB环境中处理数据&#xff0c;使用…...

ChatGPT生成的SQL注入漏洞代码竟通过了87%静态扫描器?安全团队紧急避坑指南(含检测脚本)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;ChatGPT生成的SQL注入漏洞代码竟通过了87%静态扫描器&#xff1f;安全团队紧急避坑指南&#xff08;含检测脚本&#xff09; 近期&#xff0c;某金融企业安全团队在代码审计中发现&#xff0c;一段由ChatGPT生…...

10M参数也能跑ARC与数独,Bengio团队押注「多轨迹推理」

10M 参数跑到数独 97%&#xff0c;GRAM 把递归推理改成多轨迹采样。 10M 参数&#xff0c;在大模型时代显得有些微不足道。 但 Yoshua Bengio 团队与 KAIST、Mila、NYU 研究人员提出的 GRAM&#xff0c;用这个量级的模型跑出了几组值得注意的结果。 在 Sudoku-Extreme 上准确率…...

免费文档下载终极方案:如何优雅获取百度文库等30+平台资源

免费文档下载终极方案&#xff1a;如何优雅获取百度文库等30平台资源 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档&#xff0c;但是相关网站浏览体验不好各种广告&#xff0c;各种登录验证&#xff0c;需要很多步骤才能下载文档&#xff0c;该脚本就是为…...

2026年免费去水印工具哪个好用?免费好用的去水印工具对比推荐

在2026年&#xff0c;无论是自媒体运营者、内容创作者还是普通用户&#xff0c;去水印都是日常高频操作。但面对市场上琳琅满目的去水印工具&#xff0c;要找到一款免费好用的去水印工具着实不易。本文将从多个维度对免费去水印工具对比 2026的各类产品进行详细评测&#xff0c…...

【204期】异地组网一键联机工具

想和朋友异地联机打单机游戏&#xff0c;结果发现没有公网IP连不上&#xff1f;或者居家办公想访问公司局域网里的文件&#xff0c;搞了半天搞不定&#xff1f;今天聊的这类异地组网、内网穿透工具&#xff0c;就是专门解决这些问题的。它能把一个个单独的局域网连接起来&#…...

车站安全管控升级:黎阳之光人员无感定位,让隐患早察觉、事件可追溯

车站作为人员密集流动的公共空间&#xff0c;安全管理始终是运营的核心重点。传统管理多依赖人工巡查与固定监控&#xff0c;覆盖有限、响应偏慢&#xff0c;对人员越界、违规停留、异常聚集等情况难以做到及时预警与全程追溯。黎阳之光依托自研人员无感定位技术&#xff0c;为…...

web服务器的实验(RHCE)

web服务器的实验&#xff08;RHCE&#xff09; 实验目录 ​ 实验1&#xff1a;快速搭建一个网站 ​ 实验2&#xff1a;替换网页目录 ​ 实验3&#xff1a;搭建网站使用内网穿透 ​ 实验4&#xff1a;搭建密码验证功能来访问网站数据 ​ 实验5&#xff1a;新建文件目录列表的网站…...

Spring框架30道高频面试题(详细答案版)

本套面试题涵盖Spring核心基础、IoC容器、Bean生命周期、AOP、事务管理、依赖注入、Spring循环依赖、Spring配置、底层原理等高频核心考点&#xff0c;答案精简专业、适配面试作答&#xff0c;适合Java后端求职复习。一、Spring基础核心&#xff08;1-6题&#xff09;1. 什么是…...

BinaryBomb通关后,我总结了这6个Linux调试与逆向的‘骚操作’

BinaryBomb通关后&#xff0c;我总结了这6个Linux调试与逆向的‘骚操作’ 在计算机系统基础课程中&#xff0c;BinaryBomb实验堪称是检验学生调试与逆向能力的"试金石"。作为一位刚刚通关的"拆弹专家"&#xff0c;我想分享那些教科书上不会教、却能让你效率…...

对比官方价Taotoken活动价在长期使用中的成本优势感受

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 对比官方价&#xff0c;Taotoken活动价在长期使用中的成本优势感受 效果展示类&#xff0c;基于一段时间的实际使用数据&#xff0…...