【Go-补充】Sync包
并发编程-Sync包
sync.WaitGroup
在代码中生硬的使用time.Sleep肯定是不合适的,Go语言中可以使用sync.WaitGroup来实现并发任务的同步。 sync.WaitGroup有以下几个方法:
方法名 | 功能 |
---|---|
(wg * WaitGroup) Add(delta int) | 计数器+delta |
(wg *WaitGroup) Done() | 计数器-1 |
(wg *WaitGroup) Wait() | 阻塞直到计数器变为0 |
sync.WaitGroup内部维护着一个计数器,计数器的值可以增加和减少。例如当我们启动了N 个并发任务时,就将计数器值增加N。每个任务完成时通过调用Done()方法将计数器减1。通过调用Wait()来等待并发任务执行完,当计数器值为0时,表示所有并发任务已经完成。
我们利用sync.WaitGroup将上面的代码优化一下:
var wg sync.WaitGroupfunc hello() {defer wg.Done()fmt.Println("Hello Goroutine!")
}
func main() {wg.Add(1)go hello() // 启动另外一个goroutine去执行hello函数fmt.Println("main goroutine done!")wg.Wait()
}
需要注意sync.WaitGroup是一个结构体,传递的时候要传递指针。
sync.Once
sync.Once
是 Go 语言 sync
包提供的一个同步原语,用于确保某个操作只执行一次,无论它被调用多少次或有多少个 Goroutine 同时尝试执行它。
它的主要用途包括:
1.延迟初始化 (Lazy Initialization):当你想在资源第一次被需要时才进行初始化,而不是在程序启动时就初始化,sync.Once
可以确保初始化只发生一次,从而避免重复初始化的开销。
import ("fmt""sync""time"
)var once sync.Once
var expensiveResource stringfunc getExpensiveResource() string {once.Do(func() {fmt.Println("正在初始化昂贵资源......")time.Sleep(100 * time.Millisecond) // 模拟初始化耗时expensiveResource = "这是一个昂贵的资源"})return expensiveResource
}func main() {var wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)go func() {defer wg.Done()fmt.Println(getExpensiveResource())}()}wg.Wait()fmt.Println("所有 Goroutine 已完成。")
}
在上面的例子中,"正在初始化昂贵资源..."
只会被打印一次,即使 getExpensiveResource
被多个 Goroutine 调用。
2.单例模式 (Singleton Pattern):确保一个类的实例只被创建一次。
package mainimport ("fmt""sync"
)type Singleton struct {Name string
}var (instance *Singletononce sync.Once
)func GetSingletonInstance() *Singleton {once.Do(func() {fmt.Println("create Singleton instance")instance = &Singleton{Name: "my Singleton instance"}})return instance
}func main() {var wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)go func() {defer wg.Done()s := GetSingletonInstance()fmt.Println(s.Name)}()}wg.Wait()
}
3.包级别初始化 (Package-Level Initialization):在包加载时进行一次性设置。
sync.Once
的新特性 (Go 1.21 及更高版本):
Go 1.21 引入了 OnceFunc
, OnceValue
, 和 OnceValues
,它们是 sync.Once
的封装,提供了更简洁的用法:
OnceFunc(f func()) func()
: 返回一个函数,该函数只会调用f
一次。多次调用返回的函数都会执行,但f
只会在第一次被调用。OnceValue[T any](f func() T) func() T
: 返回一个函数,该函数只会调用f
一次并缓存f
的返回值。后续调用都会返回相同的值。OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2)
: 类似于OnceValue
,但用于返回两个值。
示例:OnceValue
package mainimport ("fmt""sync"
)func main() {// 定义一个只计算一次并返回结果的函数getSumOnce := sync.OnceValue(func() int {sum := 0for i := 0; i < 1000; i++ {sum += i}fmt.Println("计算了一次:", sum)return sum})var wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go func() {defer wg.Done()result := getSumOnce() // 这里开辟10个协程 执行函数,但实际只执行了一次fmt.Printf("Goroutine %d 得到结果: %d\n", i, result)}()}wg.Wait()
}
在这个例子中,getSumOnce
函数只会在第一次被调用时执行内部的求和逻辑,并打印 “计算了一次:”,之后的调用会直接返回缓存的结果。
重要注意事项:
sync.Once
实例一旦执行过其Do
方法,就不能被重置。- 如果传递给
Do
方法的函数发生 panic,sync.Once
仍会认为该操作已完成,后续调用不会再次执行该函数。 sync.Once
不应被复制。
总而言之,sync.Once
是 Go 中一个强大且常用的同步工具,用于安全地执行一次性初始化。
sync.Map
sync.Map
是 Go 语言 sync
包提供的一个并发安全的哈希映射(map),它旨在解决 Go 内置 map
在并发访问时需要外部 sync.RWMutex
保护的性能问题,尤其是在读多写少的场景下。
Go 语言内置的 map
不是并发安全的。如果在多个 Goroutine 中同时对一个 map
进行读写操作,会导致数据竞争(data race),进而引发程序崩溃或产生不确定的行为。为了解决这个问题,通常需要使用 sync.RWMutex
来保护 map
的并发访问
var m = make(map[string]int)func get(key string) int {return m[key]
}func set(key string, value int) {m[key] = value
}func main() {wg := sync.WaitGroup{}for i := 0; i < 20; i++ {wg.Add(1)go func(n int) {key := strconv.Itoa(n)set(key, n)fmt.Printf("k=:%v,v:=%v\n", key, get(key))wg.Done()}(i)}wg.Wait()
}
上面的代码开启少量几个goroutine的时候可能没什么问题,当并发多了之后执行上面的代码就会报fatal error: concurrent map writes错误。
像这种场景下就需要为map加锁来保证并发的安全性了,Go语言的sync包中提供了一个开箱即用的并发安全版map–sync.Map。开箱即用表示不用像内置的map一样使用make函数初始化就能直接使用。同时sync.Map内置了诸如Store、Load、LoadOrStore、Delete、Range等操作方法
sync.Map 的常用方法:
-
Store(key, value interface{}): 将 key-value 对存储到 Map 中。
-
Load(key interface{}) (value interface{}, ok bool): 根据 key 加载值。如果 key 存在,返回 value 和 true;否则返回 nil 和 false。
-
LoadOrStore(key, value interface{}) (actual interface{}, loaded bool): 如果 key 存在,则返回其现有值和 true;否则,存储新的 key-value 对并返回 value 和 false。这是一个非常常用的方法,可以实现“如果不存在就存储,否则就加载”的逻辑。
-
Delete(key interface{}): 从 Map 中删除 key 及其对应的值。
-
Range(f func(key, value interface{}) bool): 遍历 Map 中的所有 key-value 对,对每个对调用提供的函数 f。如果 f 返回 false,则停止遍历。注意:在 Range 期间,Map 可能会被并发修改,所以遍历的结果可能不是一个完全一致的快照。
var m = sync.Map{}func main() {wg := sync.WaitGroup{}for i := 0; i < 10; i++ {wg.Add(1)go func(n int) {key := strconv.Itoa(n)m.Store(key, n)value, _ := m.Load(key)fmt.Printf("k=:%v,v:=%v\n", key, value)wg.Done()}(i)}wg.Wait()
}
相关文章:

【Go-补充】Sync包
并发编程-Sync包 sync.WaitGroup 在代码中生硬的使用time.Sleep肯定是不合适的,Go语言中可以使用sync.WaitGroup来实现并发任务的同步。 sync.WaitGroup有以下几个方法: 方法名功能(wg * WaitGroup) Add(delta int)计数器delta(wg *WaitGroup) Done()…...
云服务器是什么,和服务器有什么区别?
云服务器 vs 传统服务器:通俗对比 一句话总结: 云服务器是「租用」的虚拟服务器(像租房),传统服务器是「自购」的物理机器(像买房)。 1. 本质区别 对比项云服务器传统服务器物理形态虚拟的&am…...
【HTML-14】HTML 列表:从基础到高级的完整指南
列表是HTML中用于组织和展示信息的重要元素。无论是导航菜单、产品特性还是步骤说明,列表都能帮助我们以结构化的方式呈现内容。本文将全面介绍HTML中的列表类型、语法、最佳实践以及一些高级技巧。 1. HTML列表的三种类型 HTML提供了三种主要的列表类型ÿ…...

设备驱动与文件系统:01 I/O与显示器
操作系统设备驱动学习之旅——以显示器驱动为例 从这一节开始,我要学习操作系统的第四个部分,就是i o设备的驱动。今天要讲的是第26讲,内容围绕i o设备中的显示器展开,探究显示器是如何被驱动的,也就是操作系统怎样让…...
.NET 9正式发布,亮点是.NET Aspire和AI
.NET 9 正式发布:.NET Aspire 与 AI 引领新潮流 一、.NET 9 发布概览 Microsoft 正式发布了 .NET 9,这一版本堪称迄今为止最高效、最现代、最安全、最智能且性能最高的 .NET 版本。它凝聚了全球数千名开发人员一年的心血,带来了数千项性能、…...
vue+mitt的简便使用
突然注意到 onMounted 在一个组件中可以多次调用,这不得发挥一下: 把绑定/解绑的逻辑封装到同一个模块中不就简化了吗,只需要在组件中注册一下子再传递一个回调就完事了。简单的组件中甚至不用引入onMounted和onUnmounted cnpm i mitt /src/utils/emi…...
Java正则表达式完全指南
Java正则表达式完全指南 一、正则表达式基础概念1.1 什么是正则表达式1.2 Java中的正则表达式支持 二、正则表达式基本语法2.1 普通字符2.2 元字符2.3 预定义字符类 三、Java中正则表达式的基本用法3.1 编译正则表达式3.2 创建Matcher对象并执行匹配3.3 常用的Matcher方法 四、…...
Windows搭建Swift语言编译环境?如何构建ObjC语言编译环境?Swift如何引入ObjC框架?Interface Builder的历史?
目录 Windows搭建Swift语言编译环境 如何构建ObjC语言编译环境? Swift如何引入ObjC框架? Swift和ObjC中IBOutlet和IBAction代表什么? Interface Builder的历史 Xcode的“Use Storyboards"的作用? Xcode的Playground是什么? Windows搭建Swift语言编译环境 Windo…...
第七部分:第四节 - 在 NestJS 应用中集成 MySQL (使用 TypeORM):结构化厨房的原材料管理系统
在 NestJS 这样一个结构化的框架中,我们更倾向于使用 ORM (Object-Relational Mapper) 来与关系型数据库交互。ORM 就像中央厨房里一套智能化的原材料管理系统,它将数据库中的表格和行映射到我们熟悉的对象和类的实例。我们可以使用面向对象的方式来操作…...
Bug 背后的隐藏剧情
Bug 背后的隐藏剧情 flyfish 1. 「bug」:70多年前那只被拍进史书的飞蛾 故事原型:1947年哈佛实验室的「昆虫命案」 1947年的计算机长啥样?像一间教室那么大,塞满了几万根继电器(类似老式开关)ÿ…...
Golang | 搜索哨兵-对接分布式gRPC服务
哨兵(centennial)负责接待客人,直接与调用方对接。哨兵的核心组件包括service HUB和connection pool。service HUB用于与服务中心通信,获取可提供服务的节点信息。connection pool用于缓存与index worker的连接,避免每…...

智慧充电桩数字化管理平台:环境监测与动态数据可视化技术有哪些作用?
随着新能源汽车的普及,智慧充电桩作为基础设施的重要组成部分,正逐步向数字化、智能化方向发展。环境监测与动态数据可视化技术的应用,为充电桩的高效管理和运维提供了全新解决方案。通过实时采集环境参数与运行数据,并结合可视化…...
debian12.9或ubuntu,vagrant离线安装插件vagrant-libvirt
系统盘: https://mirror.lzu.edu.cn/debian-cd/12.9.0/amd64/iso-dvd/debian-12.9.0-amd64-DVD-1.iso 需要的依赖包,无需安装ruby( sudo apt install -y ruby-full ruby-dev rubygems ) : apt install -y iptables; apt install -y curl;rootdebian129:~# dpkg -l iptables …...

家政小程序开发,开启便捷生活新篇章
在快节奏的现代生活中,家务琐事常常让人分身乏术,如何高效解决家政服务需求成了众多家庭的难题。家政小程序开发,正是为解决这一痛点而生,它将为您带来前所未有的便捷生活体验。 想象一下,您只需打开手机上的家政小程…...
C++ 重载(Overload)、重写(Override)、隐藏(Hiding) 的区别
C 重载(Overload)、重写(Override)、隐藏(Hiding) 的区别 这三个概念是 C 面向对象的核心知识点,也是面试必问内容。下面我们从定义、发生条件、代码示例、底层原理全方位解析它们的区别。 一、核心区别对比表(速记版) 特性重载(Overload)…...

李臻20242817_安全文件传输系统项目报告_第14周
安全文件传输系统项目报告(第 14 周) 1. 代码链接 Gitee 仓库地址:https://gitee.com/li-zhen1215/homework/tree/master/Secure-file 代码结构说明: SecureFileTransfer/ ├── client/ # 客户端主目…...

20250531MATLAB三维绘图
MATLAB三维绘图 三维曲线:plot3功能介绍代码实现过程plot3实现效果 三维曲面空间曲面作图命令:meshmeshgrid语法示例应用meshgrid实操训练 peakspeaks 的基本用法peaks数学表达式实操训练自定义网格大小使用自定义网格 meshMATLAB代码对齐快捷键Ctrli墨西…...

深入理解C#异步编程:原理、实践与最佳方案
在现代软件开发中,应用程序的性能和响应能力至关重要。特别是在处理I/O密集型操作(如网络请求、文件读写、数据库查询)时,传统的同步编程方式会导致线程阻塞,降低程序的吞吐量。C# 的异步编程模型(async/aw…...

基于千帆大模型的AI体检报告解读系统实战:使用OSS与PDFBox实现PDF内容识别
目录 说明 前言 需求 流程说明 表结构说明 整体流程 百度智能云 注册和实名认证 创建应用 费用说明 大模型API说明 集成大模型 设计Prompt 上传体检报告 读取PDF内容 功能实现 智能评测 抽取大模型工具 功能实现 总结 说明 AI体检报告解读、病例小结或者…...
Redis缓存落地总结
最近在优化电子签系统,涉及到缓存相关的也一并优化了,写个文档做个总结,防止以后开发时又考虑不全 1、避免大key 避免缓存大PDF文件: 💡 经验值:单个Redis Value不超过10KB,集合元素不超过500…...

Spring,SpringMVC,SpringBoot
1.Spring最核心包括aop和ioc概念 AOP 能够将将哪些于业务无关的,并且大量重复的业务逻辑进行封装起来,便于减少重复代码,降低模块之间的耦合度,给未来的系统更好的可用性和可维护性。 Spring中AOP是采用动态代理,JDK代…...
npm、pnpm、yarn使用以及区别
npm 使用 安装包:在项目目录下,npm install <包名> 用于本地安装包到 node_modules 目录,并添加到 package.json 的 dependencies 中;npm install -g <包名> 用于全局安装,适用于命令行工具等。初始化项目…...
flutter加载dll 报错问题
解决flutter加载dll 报错问题 LoadLibrary 报错 126 or 193 明确一点:flutter构建exe 时默认是MSVC的。 1. 先检查dll 的位数是否满足 file ***.dll output: PE32 executable (DLL) (console) x86-64, for MS Windows, 19 sections 这种是64位的机器。 满足的话可…...

数据分析学习笔记——A/B测试
目录 前言 A/B测试中的统计学方法 假设检验 Levenes Test莱文测试 t 检验(两组均值差异) 实战案例 数据来源及参考资料 代码详解 导入数据 计算ROI Request检验 GMV检验 ROI检验 结语 前言 什么是A/B测试?说白了就是中学生物实…...
【python深度学习】Day 41 简单CNN
知识回顾 数据增强卷积神经网络定义的写法batch归一化:调整一个批次的分布,常用与图像数据特征图:只有卷积操作输出的才叫特征图调度器:直接修改基础学习率 卷积操作常见流程如下: 1. 输入 → 卷积层 → Batch归一化层…...

基于RK3568/RK3588/全志H3/飞腾芯片/音视频通话程序/语音对讲/视频对讲/实时性好/极低延迟
一、前言说明 近期收到几个需求都是做音视频通话,很多人会选择用webrtc的方案,这个当然是个不错的方案,但是依赖的东西太多,而且相关组件代码量很大,开发难度大。所以最终选择自己属性的方案,那就是推流拉…...

解决 Win11 睡眠后黑屏无法唤醒的问题
目录 一、问题描述二、解决方法1. 禁用快速启动2. 设置 Management Engine Interface3. 允许混合睡眠其他命令 4. 修复系统文件5. 更新 Windows 或驱动程序6. 其他1)更改电源选项2)刷新 Hiberfil.sys 文件3)重置电源计划4)运行系统…...

[ElasticSearch] RestAPI
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...

Linux中的shell脚本
什么是shell脚本 shell脚本是文本的一种shell脚本是可以运行的文本shell脚本的内容是由逻辑和数据组成shell脚本是解释型语言 用file命令可以查看文件是否是一个脚本文件 file filename 脚本书写规范 注释 单行注释 使用#号来进行单行注释 多行注释 使用 : " 注释内容…...

dvwa3——CSRF
LOW: 先尝试change一组密码:123456 修改成功,我们观察上面的url代码 http://localhost/DVWA/vulnerabilities/csrf/?password_new123456&password_conf123456&ChangeChange# 将password_new部分与password_conf部分改成我们想要的…...