当前位置: 首页 > 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;使用…...

LeetCode26. 删除有序数组中的重复项 27. 移除元素 35. 搜索插入位置 数组,双指针 二分查找

给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。考虑 nums 的唯一元素的数量为 k。去重后&#xf…...

实战应用:基于快马AI生成的代码,快速构建全功能在线学术期刊平台

实战应用&#xff1a;基于快马AI生成的代码&#xff0c;快速构建全功能在线学术期刊平台 最近在帮学校实验室搭建一个开源学术期刊的在线投稿系统&#xff0c;正好体验了InsCode(快马)平台的AI代码生成功能。整个过程比想象中顺利很多&#xff0c;从需求分析到可运行的原型只用…...

AssetStudio终极指南:快速免费提取Unity游戏模型、纹理与音频资源

AssetStudio终极指南&#xff1a;快速免费提取Unity游戏模型、纹理与音频资源 【免费下载链接】AssetStudio 项目地址: https://gitcode.com/gh_mirrors/asse/AssetStudio AssetStudio是一款功能强大的开源工具&#xff0c;专为Unity游戏资源提取设计&#xff0c;能够轻…...

Vue2项目实战:集成西瓜播放器xgplayer实现企业级视频播放组件

1. 为什么选择xgplayer做企业级视频播放方案 在在线教育平台这类对视频播放要求较高的场景中&#xff0c;播放器的选择直接影响用户体验和开发效率。我经历过多个项目的实战验证&#xff0c;西瓜播放器xgplayer确实是个不错的选择。它不像某些开源播放器那样需要折腾各种兼容性…...

手把手教你用Cline插件零成本调用AI Ping的GLM-4.7,5分钟搞定一个React组件

5分钟实战&#xff1a;用Cline插件调用GLM-4.7生成React表单组件 最近在帮团队优化一个后台管理系统时&#xff0c;发现表单页面的重复开发消耗了大量时间。直到同事推荐了AI Ping的GLM-4.7模型配合VSCode的Cline插件&#xff0c;才真正体会到AI辅助编程的"开箱即用"…...

M2LOrder模型Typora写作辅助插件开发:实时监测文章情感基调

M2LOrder模型Typora写作辅助插件开发&#xff1a;实时监测文章情感基调 不知道你有没有过这样的经历&#xff1a;写了一篇技术文章&#xff0c;自己读起来总觉得哪里不对劲&#xff0c;但又说不出来具体问题。或者写产品文案时&#xff0c;明明想表达积极向上的情绪&#xff0…...

瑞芯微RK3399固件急救指南:用upgrade_tool搞定系统崩溃后的快速还原

RK3399固件灾难恢复实战&#xff1a;从分区表重建到全系统还原 当一块搭载RK3399的开发板因固件损坏而变砖时&#xff0c;那种面对黑屏的无力感&#xff0c;相信每个嵌入式开发者都深有体会。去年我们产线就遭遇过因批量升级失败导致30台设备集体罢工的紧急状况&#xff0c;正…...

新手零基础入门:在快马平台用AI生成你的首个龙虾部署项目

新手零基础入门&#xff1a;在快马平台用AI生成你的首个龙虾部署项目 作为一个刚接触容器化开发的新手&#xff0c;第一次听说"龙虾部署"这个概念时&#xff0c;我完全摸不着头脑。后来才知道&#xff0c;这其实就是Docker容器化部署的一种形象说法。今天我想分享一…...

DDS信号发生器设计避坑指南:基于FPGA的AD9767驱动常见问题与优化技巧

FPGA驱动AD9767实战避坑手册&#xff1a;从时序优化到DDS精度提升的进阶技巧 当你在深夜调试FPGA与AD9767的接口时&#xff0c;示波器上那些不规则的毛刺和频率偏差是否曾让你抓狂&#xff1f;作为一款经典的高速14位DAC芯片&#xff0c;AD9767在信号发生、通信系统等领域应用广…...

如何轻松实现单机游戏分屏多人:Nucleus Co-Op完整指南

如何轻松实现单机游戏分屏多人&#xff1a;Nucleus Co-Op完整指南 【免费下载链接】nucleuscoop Starts multiple instances of a game for split-screen multiplayer gaming! 项目地址: https://gitcode.com/gh_mirrors/nu/nucleuscoop 还在为找不到联机伙伴而烦恼吗&a…...