【Go-补充】Sync包

article/2025/8/13 19:10:56

并发编程-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 调用。

QQ_1748327479922

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()
}

QQ_1748327550850

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 函数只会在第一次被调用时执行内部的求和逻辑,并打印 “计算了一次:”,之后的调用会直接返回缓存的结果。

QQ_1748327857447

重要注意事项:

  • 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()
}

QQ_1748328489525

上面的代码开启少量几个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()
}

QQ_1748328657099


http://www.hkcw.cn/article/JjTjsZKrQC.shtml

相关文章

M-OFDM模糊函数原理及仿真

文章目录 前言一、M序列二、M-OFDM 信号1、OFDM 信号表达式2、模糊函数表达式 三、MATLAB 仿真1、MATLAB 核心源码2、仿真结果①、m-OFDM 模糊函数②、m-OFDM 距离分辨率③、m-OFDM 速度分辨率④、m-OFDM 等高线图 四、资源自取 前言 本文进行 M-OFDM 的原理讲解及仿真&#x…

《C++初阶之入门基础》【C++的前世今生】

【C的前世今生】目录 前言&#xff1a;---------------起源---------------一、历史背景二、横空出世---------------发展---------------三、标准立世C98&#xff1a;首个国际标准版本C03&#xff1a;小修订版本 四、现代进化C11&#xff1a;现代C的开端C14&#xff1a;对C11的…

长上下文推理新范式!QwenLong-L1如何通过强化学习突破大模型语境局限?

长上下文推理新范式&#xff01;QwenLong-L1如何通过强化学习突破大模型语境局限&#xff1f; 在大模型推理能力不断精进的今天&#xff0c;长上下文处理仍是亟待突破的难题。本文介绍的QwenLong-L1框架&#xff0c;借助渐进式语境扩展与强化学习&#xff0c;成功让大模型在长…

git 学习

目录 关于git 版本管理概述 git的优点 一 下载&#xff0c;安装git 二 使用git 的处理流程 三 学习基本的git命令 1 git初始化 2 查看 状态 3 提交的缓存区 4回退到工作区 关于git 版本管理概述 码云&#xff1a;Gitee - 基于 Git 的代码托管和研发协作平台 git 是用…

中国风展示工作总结商务通用PPT模版

中国风展示工作总结商务通用PPT模版&#xff1a;中国风商务通用PPT 模版https://pan.quark.cn/s/42ad18c010d4

gitflow

gitflow 1. 各个分支介绍 master分支: 源代码 HEAD始终反映出生产就绪状态。仅包含 生产环境可发布的代码&#xff0c;每个提交对应一个正式版本&#xff08;通过 git tag 打版本号&#xff09;一般情况下&#xff0c;只允许合并(如从 release 或 hotfix 分支合并), 禁止直接提…

Python训练营---Day42

DAY 42 Grad-CAM与Hook函数 知识点回顾 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 作业&#xff1a;理解下今天的代码即可 1、回调函数 回调函数&#xff08;Callback Function&#xff09;是一种特殊的函数&#xff0c;它作为参数传递给另一个函数&#…

Git远程操作

目录 1. 理解分布式版本控制系统 2. 远程仓库 3. 新建远程仓库 4. 克隆远程仓库 4.1 使用HTTPS方式&#xff1a; 4.2 使用SSH方式&#xff1a; 5. 向远程仓库推送 总结&#xff1a; 问题&#xff1a; 6. 拉取远程仓库 7. 配置Git 7.1 忽略特殊文件 8. 给命令配置别…

SolidWorks软件的安装与卸载

文章目录 软件的下载途径软件的安装软件的卸载 简介&#xff1a;这篇文章介绍了SolidWorks软件的安装与卸载&#xff0c;步骤是比较繁琐的&#xff0c;但照着步骤一步一步的来15分钟就能安装成功。这里要特别的注意一点的是&#xff0c;文件的安装位置一定要集中&#xff08;别…

Python 验证码识别(使用pytesseract库)

文章目录 摘要1、安装Tesseract-OCR2、在python中使用安装依赖 3、本地图片识别4、结合playwright动态识别网站验证码 摘要 python中使用pytesseract库进行ocr识别&#xff0c;需要安装Tesseract-OCR&#xff0c;通过指定pytesseract.tesseract_cmd路径&#xff0c;可以将esser…

日志与策略模式

什么是设计模式 IT行业这么火, 涌入的人很多. 俗话说林子大了啥鸟都有. 大佬和菜鸡们两极分化的越来越严重. 为了让菜鸡们不太拖大佬的后腿, 于是大佬们针对一些经典的常见的场景, 给定了一些对应的解决方案, 这个就是 设计模式 日志认识 计算机中的日志是记录系统和软件运行中…

ToolsSet之:XML工具

ToolsSet是微软商店中的一款包含数十种实用工具数百种细分功能的工具集合应用&#xff0c;应用基本功能介绍可以查看以下文章&#xff1a; Windows应用ToolsSet介绍https://blog.csdn.net/BinField/article/details/145898264 ToolsSet中Text菜单下的XML Tool工具是一个Xml工…

2025年目前最新版本Android Studio自定义xml预览的屏幕分辨率

一、前言 在实际开发项目当中&#xff0c;我们的设备的分辨率可能会比较特殊&#xff0c;AS并没有自带这种屏幕分辨率的设备&#xff0c;但是我们又想一边编写XML界面&#xff0c;一边实时看到较为真实的预览效果&#xff0c;该怎么办呢&#xff1f;在早期的AS版本中&#xff…

sql知识梳理(超全,超详细,自用)

目录 通识 查询的基本语法 数据库&#xff08;database&#xff09;操作 表&#xff08;table&#xff09;的操作 表中列的操作 索引操作 表中行的操作 insert into语句 update语句 删除语句 select语句 表与表之间的关系 连接查询 子查询 视图 数据备份与还原 …

数据分析图表类型及其应用场景

说明&#xff1a;顶部HTML文件下载后可以直接查看&#xff0c;带有示图。 摘要 数据可视化作为现代数据分析的核心环节&#xff0c;旨在将复杂、抽象的数据转化为直观、易懂的图形形式。这种转化显著提升了业务决策能力&#xff0c;优化了销售与营销活动&#xff0c;开辟了新…

1、树莓派更换软件下载源

树莓派官方系统raspbian自带的是国外的软件源&#xff0c;在国内使用经常会遇到无法下载软件的问题。 以下是把raspbian系统&#xff08;buster版本&#xff09;的下载源改为阿里云软件源的方法。 1、修改sources.list文件 sudo nano /etc/apt/sources.list 将初始化中的代…

TDengine 集群容错与灾备

简介 为了防止数据丢失、误删操作&#xff0c;TDengine 提供全面的数据备份、恢复、容错、异地数据实时同步等功能&#xff0c;以保证数据存储的安全。本节简要说明 TDengine 中的容错与灾备。 容错 TDengine 支持 WAL 机制&#xff0c;实现数据的容错能力&#xff0c;保证数…

第十五章 访问控制

系列文章目录 第一章 总体概述 第二章 在实体机上安装ubuntu 第三章 Windows远程连接ubuntu 第四章 使用Docker安装和运行EMQX 第五章 Docker卸载EMQX 第六章 EMQX客户端MQTTX Desktop的安装与使用 第七章 EMQX客户端MQTTX CLI的安装与使用 第八章 Wireshark工具的安装与使用 …

LeetCode算法题 (搜索二维矩阵)Day18!!!C/C++

https://leetcode.cn/problems/search-a-2d-matrix/description/ 一、题目分析 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 ta…

基于谷歌ADK的智能客服系统简介

Google的智能体开发工具包&#xff08;Agent Development Kit&#xff0c;简称ADK&#xff09;是一个开源的、以代码为中心的Python工具包&#xff0c;旨在帮助开发者更轻松、更灵活地构建、评估和部署复杂的人工智能智能体&#xff08;AI Agent&#xff09;。ADK 是一个灵活的…