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

Go:使用共享变量实现并发

竞态

在串行程序中,步骤执行顺序由程序逻辑决定;而在有多个 goroutine 的并发程序中,不同 goroutine 的事件先后顺序不确定,若无法确定两个事件先后,它们就是并发的。若一个函数在并发调用时能正确工作,称其为并发安全。当类型的所有可访问方法和操作都是并发安全时,该类型为并发安全类型。并发安全的类型并非普遍存在,若要在并发中安全访问变量,需限制变量仅在一个 goroutine 内存在,或维护更高层的互斥不变量。

package bankvar balance intfunc Deposit(amount int) { balance = balance + amount }
func Balance() int { return balance }// Alice:
go func() {bank.Deposit(200)  // A1fmt.Println("=", bank.Balance()) // A2
}()
// Bob:
go func() {bank.Deposit(100)  // B
}()

竞态是指多个 goroutine 按交错顺序执行时,程序无法给出正确结果的情形。它对程序是致命的,可能潜伏在程序中,出现频率低,且难以再现和分析。以银行账户程序为例,在并发调用DepositBalance函数时,若多个 goroutine 交错执行,可能出现数据竞态,导致账户余额计算错误,如出现存款丢失等情况。数据竞态发生在两个或多个 goroutine 并发读写同一个变量,且至少其中一个是写入时。当变量类型大于机器字长(如接口、字符串或 slice)时,数据竞态问题会更复杂。

避免数据竞态的方法

  • 不修改变量:对于延迟初始化的 map,若并发调用访问可能存在数据竞态。但如果在创建其他 goroutine 之前,用完整数据初始化 map 且不再修改,那么多个 goroutine 可安全并发调用相关函数读取 map。
package bankvar deposits = make(chan int) // 发送存款额
var balances = make(chan int) // 接收余额func Deposit(amount int) { deposits <- amount }
func Balance() int { return <-balances }func teller() {var balance intfor {select {case amount := <-deposits:balance += amountcase balances <- balance:}}
}func init() {go teller() // 启动监控goroutine
}
  • 避免多个 goroutine 访问同一变量:通过将变量限制在单个 goroutine 内部访问来避免竞态。如 Web 爬虫中主 goroutine 是唯一能访问seen map 的,消息服务器中broadcaster goroutine 是唯一能访问clients map 的。还可通过监控 goroutine 来限制对共享变量的访问,如银行案例中用teller goroutine 限制balance变量的并发访问 。
  • 允许多个 goroutine 访问,但同一时间只有一个可访问:通过互斥机制实现。

互斥锁:sync.Mutex

// 使用通道实现二进制信号量保护balance
var (sema    = make(chan struct{}, 1) // 用来保护 balance 的二进制信号量balance int
)
func Deposit(amount int) {sema <- struct{}{} // 获取令牌balance = balance + amount<-sema // 释放令牌
}
func Balance() int {sema <- struct{}{} // 获取令牌b := balance<-sema // 释放令牌return b
}

为保证同一时间最多有一个 goroutine 能访问共享变量,可使用容量为 1 的通道作为二进制信号量。

由于互斥锁模式应用广泛,Go 语言sync包提供了Mutex类型来支持这种模式,Lock方法用于获取令牌(上锁),Unlock方法用于释放令牌(解锁)。

// 使用sync.Mutex实现互斥锁保护balance
import "sync"
var (mu      sync.Mutex // 保护 balancebalance int
)
func Deposit(amount int) {mu.Lock()balance = balance + amountmu.Unlock()
}
func Balance() int {mu.Lock()b := balancemu.Unlock()return b
}

示例:以银行账户程序为例,定义musync.Mutex类型来保护balance变量 。在DepositBalance函数中,通过先调用mu.Lock()获取互斥锁,访问或修改balance变量,最后调用mu.Unlock()释放锁 ,确保共享变量不会被并发访问 。这种函数、互斥锁、变量的组合方式称为监控(monitor)模式。

func Balance() int {mu.Lock()defer mu.Unlock()return balance
}

LockUnlock之间的代码区域称为临界区域,此区域内可自由读写共享变量 。一个 goroutine 在使用完互斥锁后应及时释放,对于有多个分支(尤其是错误分支)的复杂函数,可使用defer语句延迟执行Unlock,将临界区域扩展到函数结尾,保证锁能正确释放 ,即使在临界区域崩溃时也能正常执行解锁操作 。

原子操作与互斥锁的应用

// 不正确的Withdraw实现示例
func Withdraw(amount int) bool {Deposit(-amount)if Balance() < 0 {Deposit(amount)return false // 余额不足}return true
}// 错误的Withdraw加锁尝试示例
func Withdraw(amount int) bool {mu.Lock()defer mu.Unlock()Deposit(-amount)if Balance() < 0 {Deposit(amount)return false // 余额不足}return true
}// 正确的Withdraw实现示例
func Withdraw(amount int) bool {mu.Lock()defer mu.Unlock()deposit(-amount)if balance < 0 {deposit(amount)return false // 余额不足}return true
}func Deposit(amount int) {mu.Lock()defer mu.Unlock()deposit(amount)
}func Balance() int {mu.Lock()defer mu.Unlock()return balance
}// 这个函数要求已获取互斥锁
func deposit(amount int) { balance += amount }

Withdraw函数为例,最初版本因不是原子操作(包含多个串行操作且未对整个操作上锁)存在问题,在尝试超额提款时可能导致余额异常 。改进版本应在整个操作开始时申请一次互斥锁 ,但直接在Withdraw中嵌套调用已使用互斥锁的Deposit函数会因互斥锁不可再入导致死锁 。最终解决方案是将Deposit函数拆分为不导出的deposit函数(假定已获取互斥锁并完成业务逻辑)和导出的Deposit函数(负责获取锁并调用deposit ),从而正确实现Withdraw函数 。使用互斥锁时,应确保互斥锁本身及被保护的变量都不被导出 ,以维持并发中的不变性

读写互斥锁:sync.RWMutex

var mu sync.RWMutex
var balance intfunc Balance() int {mu.RLock() // 读锁defer mu.RUnlock()return balance
}

以 Bob 频繁查询账户余额为例,银行的Balance函数只是读取变量状态,多个Balance请求可并发运行,只要DepositWithdraw请求不同时运行即可 。为满足这种场景需求,需要一种特殊的锁,即多读单写锁,Go 语言中的sync.RWMutex可提供此功能。

  • 读锁操作:定义musync.RWMutex类型 ,在Balance函数中,通过调用mu.RLock()获取读锁(共享锁),使用defer mu.RUnlock()延迟释放读锁,确保在函数结束时释放锁 ,这样多个读操作可并发进行。
  • 写锁操作Deposit函数等写操作函数,仍通过调用mu.Lock()获取写锁(互斥锁),mu.Unlock()释放写锁 ,保证写操作时的独占访问权限。

注意事项

  • RLock仅适用于临界区域内对共享变量无写操作的情形 ,因为有些看似只读的函数可能会更新内部变量,若不确定应使用独占版本的Lock
  • 当绝大部分 goroutine 都在获取读锁且锁竞争激烈时,RWMutex才有优势,因为其内部簿记工作更复杂,在竞争不激烈时比普通互斥锁慢

内存同步

以银行账户的Balance函数为例,其需要互斥锁不仅是防止操作交错,还涉及内存同步问题。现代计算机多处理器有本地内存缓存,写操作先缓存在处理器中,刷回内存顺序可能与 goroutine 写入顺序不一致。通道通信、互斥锁等同步原语可使处理器将累积写操作刷回内存并提交,保证执行结果对其他处理器上的 goroutine 可见。

var x, y int
go func() {x = 1fmt.Print("y:", y, " ")
}()
go func() {y = 1fmt.Print("x:", x, " ")
}()

通过代码示例,两个 goroutine 并发访问共享变量xy,在未使用互斥锁时存在数据竞态,预期输出为y:0 x:1x:0 y:1x:1 y:1y:1 x:1这四种情况之一 。但实际可能出现x:0 y:0y:0 x:0这种意外输出 。原因在于单个 goroutine 内语句执行顺序一致,但在无同步措施时,不同 goroutine 间无法保证事件顺序一致 。编译器可能因赋值和打印对应不同变量,交换语句执行顺序,CPU 也可能因缓存等问题导致一个 goroutine 的写入操作对另一个 goroutine 的Print语句不可见 。

解决:为避免这些并发问题,可采用成熟模式,将变量限制在单个 goroutine 中;对于其他变量,使用互斥锁进行同步 。

延迟初始化sync.Once

var icons map[string]image.Image
func loadIcons() {icons = map[string]image.Image{"spades.png":  loadIcon("spades.png"),"hearts.png":  loadIcon("hearts.png"),"diamonds.png": loadIcon("diamonds.png"),"clubs.png":   loadIcon("clubs.png"),}
}
// 并发不安全版本
func Icon(name string) image.Image {if icons == nil {loadIcons() // 一次性地初始化}return icons[name]
}

延迟昂贵的初始化步骤到实际需要时进行,可避免增加程序启动延时。以icons变量为例,初始版本在Icon函数中检测icons是否为空,若为空则调用loadIcons进行一次性初始化 ,但此方式在并发调用Icon时不安全。

var mu sync.Mutex // 保护 icons
var icons map[string]image.Image// 并发安全版本(使用普通互斥锁)
func Icon(name string) image.Image {mu.Lock()defer mu.Unlock()if icons == nil {loadIcons()}return icons[name]
}var mu sync.RWMutex // 保护 icons
var icons map[string]image.Image// 并发安全版本(使用读写互斥锁)
func Icon(name string) image.Image {mu.RLock()if icons!= nil {icon := icons[name]mu.RUnlock()return icon}mu.RUnlock()mu.Lock()if icons == nil { // 必须重新检查nil值loadIcons()}icon := icons[name]mu.Unlock()return icon
}

在无显式同步情况下,编译器和 CPU 可能重排loadIcons语句执行顺序,导致一个 goroutine 发现icons不为nil时,初始化可能尚未真正完成 。使用互斥锁可解决同步问题,如用sync.Mutex保护icons变量 ,但这会限制并发访问,即使初始化完成且不再更改,也会阻止多个 goroutine 并发读取 。使用sync.RWMutex虽能改善并发读问题,但代码复杂且易出错 。

var loadIconsOnce sync.Once
var icons map[string]image.Image// 并发安全版本(使用sync.Once)
func Icon(name string) image.Image {loadIconsOnce.Do(loadIcons)return icons[name]
}

sync.Once为一次性初始化问题提供简化方案 。它包含布尔变量记录初始化是否完成,以及互斥量保护相关数据 。OnceDo方法以初始化函数为参数 ,首次调用Do时,锁定互斥量并检查布尔变量,若为假则调用初始化函数并将变量设为真,后续调用相当于空操作 。通过使用sync.Once,可确保变量在正确构造之前不被其他 goroutine 访问,避免竞态问题 。

竞态检测器

Go 语言运行时和工具链提供竞态检测器,用于检测并发编程中的数据竞态问题。在go buildgo rungo test命令中添加-race参数即可启用 。启用后,编译器会构建修改后的版本,记录运行时对共享变量的访问,以及读写变量的 goroutine 标识,还会记录同步事件(如go语句、通道操作、互斥锁调用、WaitGroup调用等 )。

竞态检测器通过研究事件流,找出一个 goroutine 写入变量后,无同步操作时另一个 goroutine 读写该变量的情况,即数据竞态 。检测到竞态后,会输出包含变量标识、读写 goroutine 调用栈的报告,帮助定位问题 。

它只能检测运行时发生的竞态,无法保证程序绝对不会发生竞态 。为获得最佳检测效果,测试应包含并发使用包的场景 。由于增加了额外簿记工作,带竞态检测功能的程序运行时需更长时间和更多内存,但对于排查不常发生的竞态,能节省大量调试时间 。

goroutine 和线程

可增长的栈

每个 OS 线程都有固定大小的栈内存,通常为 2MB ,用于保存在函数调用期间正在执行或临时暂停函数中的局部变量。但这个固定大小存在弊端,对于简单的 goroutine(如仅等待WaitGroup或关闭通道 ),2MB 栈内存浪费;对于复杂深度递归函数,固定大小栈又不够用,且无法兼顾空间效率和支持更深递归。

goroutine 在生命周期开始时栈很小,典型为 2KB ,也用于存放局部变量。与 OS 线程不同,goroutine 的栈可按需增大和缩小,大小限制可达 1GB ,比线程栈大几个数量级,能更灵活适应不同场景,极少的 goroutine 才会用到这么大栈。

goroutine调度

OS 线程由 OS 内核调度。每隔几毫秒,硬件时钟中断触发 CPU 调用调度器内核函数 。该函数暂停当前运行线程,保存寄存器信息到内存,选择下一个运行线程,恢复其注册表信息后继续执行 。此过程涉及完整上下文切换,包括保存和恢复线程状态、更新调度器数据结构,因内存访问及 CPU 周期消耗,操作较慢 。

Go 运行时有自己的调度器,采用 m:n 调度技术(将 m 个 goroutine 复用 / 调度到 n 个 OS 线程 )。与内核调度器不同,Go 调度器不由硬件时钟定期触发,而是由特定 Go 语言结构触发 ,如 goroutine 调用time.Sleep、被通道阻塞或进行互斥量操作时,调度器将其设为休眠模式,转而运行其他 goroutine,直到可唤醒该 goroutine 。由于无需切换到内核语境,调度 goroutine 成本比调度线程低很多 。

GOMAXPROCS

Go 调度器通过GOMAXPROCS参数确定同时执行 Go 代码所需的 OS 线程数量 ,默认值为机器上的 CPU 数量 。例如在 8 核 CPU 机器上,调度器会将 Go 代码调度到 8 个 OS 线程上执行(它是 m:n 调度中的 n )。处于休眠、被通道阻塞的 goroutine 不占用线程,阻塞在 I/O 及系统调用或调用非 Go 语言函数的 goroutine 虽需独立 OS 线程,但该线程不计入GOMAXPROCS

for {go fmt.Print(0)fmt.Print(1)
}
// $ GOMAXPROC=1 go run hacker-cliche.go  11111111111111111118008000000000000001111...
// $ GOMAXPROCS=2 go run hacker-cliche.go 01010101010101010101100110010101101001010...

可通过GOMAXPROCS环境变量或runtime.GOMAXPROCS函数显式控制该参数 。文中通过一个不断输出 0 和 1 的小程序示例展示其效果 ,当GOMAXPROCS=1时,每次最多一个 goroutine 运行,主 goroutine 和输出 0 的 goroutine 交替执行;当GOMAXPROCS=2时,两个 goroutine 可同时运行 。由于影响 goroutine 调度因素众多且运行时不断变化,实际结果可能不同。

goroutine没有标识

在多数支持多线程的操作系统和编程语言中,当前线程有独特标识,通常为整数或指针 。利用此标识可构建线程局部存储,即一个以线程标识为键的全局 map,使每个线程能独立存储和获取值,不受其他线程干扰 。

goroutine 没有可供程序员访问的标识 ,这是设计选择。因为线程局部存储易被滥用,如 Web 服务器使用支持线程局部存储的语言时,很多函数通过访问该存储查找 HTTP 请求信息,会导致类似过度依赖全局变量的 “超距作用”,使函数行为不仅取决于参数,还与运行线程标识有关,在需要改变线程标识(如使用工作线程 )时,函数行为会变得不可预测 。

Go 语言鼓励简单编程风格,函数行为应仅由显式指定参数决定,这样程序更易阅读,且在将函数子任务分发到多个 goroutine 时,无需考虑 goroutine 标识问题 。

参考资料:《Go程序设计语言》

相关文章:

Go:使用共享变量实现并发

竞态 在串行程序中&#xff0c;步骤执行顺序由程序逻辑决定&#xff1b;而在有多个 goroutine 的并发程序中&#xff0c;不同 goroutine 的事件先后顺序不确定&#xff0c;若无法确定两个事件先后&#xff0c;它们就是并发的。若一个函数在并发调用时能正确工作&#xff0c;称…...

私域流量运营:如何高效处理海量社群订单?

电商行业进入存量竞争时代&#xff0c;私域流量正悄然改写商业规则。这个被企业主们频频提及的概念&#xff0c;本质上是在构建自主可控的用户资产池——就像知名茶饮品牌「喜茶」通过会员系统沉淀3000万粉丝&#xff0c;实现复购率提升35%的数字化转型。当企业微信对话框、社群…...

【TI MSPM0】ADC进阶学习

一、学习内容 二、ADC配置讲解 转换时钟来源于本地的80mhz的振荡器&#xff0c;使得高速的12bits的转换可以达到4mhz的采样率 ADC转换过程分两个步骤&#xff0c;一是采样&#xff0c;二是转换 因此&#xff0c;配置中的是采样时钟 真正决定采样速率的是conversion clock 决定…...

一文读懂WPF系列之MVVM

WPF MVVM 什么是MVVMWPF为何使用MVVM机制WPFMVVM 的实现手段 INotifyPropertyChanged​数据绑定的源端通知​​原理 PropertyChanged事件双向绑定的完整条件常见疑惑问题 什么是MVVM 翻译全称就是 model-view-viewmodel 3部分内容 以wpf的概念角度来解释就是 数据库数据源模型…...

WPF静态资源StaticResource和动态资源DynamicResource有什么区别,x:Static又是什么意思?

什么叫WPF的资源(Resource) 资源是保存在可执行文件中的一种不可执行数据。WPF中资源用ResourceDictionary类表示&#xff0c;这个类就是一个字典&#xff0c;字典的key和value都是object类型。所以在WPF中&#xff0c;资源可以可以是图像、字符串等所有的任意CLR对象&#xf…...

vue3环境搭建、nodejs22.x安装、yarn 1全局安装、npm切换yarn 1、yarn 1 切换npm

vue3环境搭建 node.js 安装 验证nodejs是否安装成功 # 检测node.js 是否安装成功----cmd命令提示符中执行 node -v npm -v 设置全局安装包保存路径、全局装包缓存路径 在node.js 安装路径下 创建 node_global 和 node_cache # 设置npm全局安装包保存路径&#xff08;新版本…...

配置HADOOP_HOME环境变量和maven_HOME环境变量

1.右击此电脑&#xff0c;选择“属性” 2.选择“高级系统配置” 3.选择“环境变量” 4.在“系统变量”下新建两个系统变量 注意&#xff1a;变量值要“浏览目录”选择你存放hadoop的文件和maven的文件 5.在“系统变量”里双击Path”&#xff0c;在里面新建两个变量...

计算机网络:实验五路由器的应用

实验五路由器的应用 1.1实验目的 掌握路由器的应用&#xff1b;熟悉路由器的基本配置方法。 1.2实验要求 学生提前准备好实验报告&#xff0c;预习并熟悉实验步骤&#xff1b;遵守实验室纪律&#xff0c;在规定的时冋内完成要求的内容。 1.3 实验内容与步骤 1、假设企业网…...

【正点原子STM32MP257连载】第四章 ATK-DLMP257B功能测试——RS232接口测试

1&#xff09;实验平台&#xff1a;正点原子ATK-DLMP257B开发板 2&#xff09;浏览产品&#xff1a;www.alientek.com 3&#xff09;全套实验源码手册视频下载&#xff1a;正点原子资料下载中心 文章目录 第四章 ATK-DLMP257B功能测试——RS232接口测试 第四章 ATK-DLMP257B功能…...

考研单词笔记 2025.04.15

addition n添加&#xff0c;增加&#xff0c;加法 additional a附加的&#xff0c;额外的 in addition 另外&#xff1b;除此之外&#xff1b;加之 augment v增加&#xff0c;增强&#xff0c;提高 explode v急剧增长&#xff0c;爆炸&#xff0c;爆发&#xff0c;迸发 gr…...

zkmall模块商城:B2C 场景下 Vue3 前端性能优化的广度探索与实践

ZKmall作为面向B2C场景的模块化电商平台&#xff0c;其前端性能优化在Vue3框架下的实践融合了架构设计、渲染机制与业务特性&#xff0c;形成了一套多维度的优化体系。以下从技术实现与业务适配两个维度展开分析&#xff1a; 一、Vue3响应式系统深度适配 ​Proxy驱动的精准更新…...

WebSocket 技术详解

引言 在现代Web应用中&#xff0c;实时通信已经成为不可或缺的一部分。想象一下聊天应用、在线游戏、股票交易平台或协作工具&#xff0c;这些应用都需要服务器能够即时将更新推送给客户端&#xff0c;而不仅仅是等待客户端请求。WebSocket技术应运而生&#xff0c;它提供了一…...

微服务即时通信系统---(四)框架学习

目录 ElasticSearch 介绍 安装 安装kibana ES客户端安装 头文件包含和编译时链接库 ES核心概念 索引(Index) 类型(Type) 字段(Field) 映射(mapping) 文档(document) ES对比MySQL Kibana访问ES测试 创建索引库 新增数据 查看并搜索数据 删除索引 ES…...

日常记录-CentOS 9安装java17

文章目录 前言一、手动安装 Oracle JDK 17 或 OpenJDK 17&#xff08;适合自定义路径&#xff09;二、使用 CentOS 9 系统包安装 OpenJDK 17&#xff08;简单稳定&#xff09;三、使用 SDKMAN&#xff08;管理多个版本&#xff09;总结 前言 CentOS 9安装java17 一、手动安装 …...

Python 导出 PDF(ReportLab )

文章目录 1. ReportLab 使用1.1. 安装 ReportLab1.2. 创建 PDF 文件1.3. 使用文档模板 DocTemplate1.4. 使用页面模板 PageTemplate1.5. 继承 BaseDocTemplate1.6. 使用 SimpleDocTemplate1.7. 继承Canvas1.8. 直接使用Canvas 2. 字体与编码3. PLATYPUS - 页面布局和排版3.1. 设…...

私域运营的底层逻辑:从流量到留存的进阶之路

私域流量已成为企业营销的新战场&#xff0c;但盲目跟风只会事倍功半。 接下来&#xff0c;我将深入剖析私域运营的底层逻辑&#xff0c;从几个关键环节&#xff0c;助你构建高效稳定的私域体系。 一、价值优先&#xff1a;以用户需求为核心 私域运营并非简单的粉丝积累&…...

【数据结构 · 初阶】- 带头双向循环链表

目录 1.尾插 2.初始化 3.尾删、头插、头删 4.查找&#xff0c;返回 pos 指针 5.pos 前插入 优化头插&#xff0c;直接复用 优化尾插&#xff0c;直接复用 6.pos 位删除 头删尾删简化 7.销毁 整体代码 List.h List.c Test.c 循环&#xff1a;1.尾 next 指向哨兵位…...

Cube IDE常用快捷键

STM32CubeIDE常用快捷键 STM32CubeIDE快捷键很多&#xff0c;可以通过 Help > Show Active Keybindings… 查看当前可用快捷键&#xff1b;也可以在 Window > Preferences > General > Keys 中查看修改快捷键 快捷键快捷键说明Ctrl/注释行/取消注释行CtrlD删除行…...

C++开发中的DUMP文件:解决崩溃与性能问题的利器(全文字数2w+)

[外链图片转存中…(img-mf6LznjF-1744717065188)] 文章目录 前言为什么需要了解DUMPDUMP在C开发中的重要性 一、DUMP基础概念1. 什么是DUMP文件2. DUMP文件的类型3. DUMP文件的作用&#xff08;1&#xff09;调试程序崩溃&#xff08;2&#xff09;分析程序性能&#xff08;3&a…...

Golang|接口并发测试和压力测试

文章目录 这里出现某些奖品和数据库中库存量不一致的问题原因就是在并发的情况下&#xff0c;sync.Map仍然会出现脏写问题&#xff0c;就是在同时操作下的操作覆盖问题可以先把数据放到channel里&#xff0c;然后用一个单一的协程负责读取channel并写入map...

解决 Maven 500 错误:无法传输 maven-metadata.xml 文件

在使用 Maven 构建和管理 Java 项目时&#xff0c;可能会遇到类似以下的错误信息&#xff1a; [WARNING] Could not transfer metadata com.ha:xxx-model:2025.0.1.SNAPSHOT/maven-metadata.xml from/to public (http://xxx.xx.xx.xx/repository/maven-public): status code: …...

鸿蒙应用开发—鸿蒙app一键安装脚本

背景 当鸿蒙App开发完后需要提测&#xff0c;如何将App文件发给QA安装测试&#xff0c;是一件麻烦事&#xff0c;因为鸿蒙App并不能像Android Apk那样可以直接安装到设备中&#xff0c;能想到的方式有&#xff1a; 直接叫测试拿手机过来安装让测试安装DevEco Studio 拉代码编…...

opencv二值化实验

二值化实验 1二值化说明2 阈值法&#xff08;THRESH_BINARY&#xff09;3.反阈值法&#xff08;THRESH_BINARY_INV&#xff09;4截断阈值法&#xff08;THRESH_TRUNC&#xff09;5 低阈值零处理&#xff08;THRESH_TOZERO&#xff09;6 超阈值零处理&#xff08;THRESH_TOZERO_…...

3DGS之渲染管线

渲染管线&#xff08;Rendering Pipeline&#xff09;是计算机图形学中将三维场景转换为二维屏幕图像的核心流程&#xff0c;涉及CPU与GPU的分工协作。计算机图形学把渲染管线分为三个阶段&#xff1a;应用程序阶段、几何阶段、光栅化阶段。渲染管线的一般流程是&#xff1a;顶…...

C#设计模式-状态模式

状态模式案例解析&#xff1a;三态循环灯的实现 案例概述 本案例使用 状态模式&#xff08;State Pattern&#xff09; 实现了一个 三态循环灯 的功能。每点击一次按钮&#xff0c;灯的状态会按顺序切换&#xff08;状态1 → 状态2 → 状态3 → 状态1...&#xff09;&#xff…...

泛微相关文档以及相关安装包下载

泛微相关文档以及相关安装包下载 泛微相关安装包下载泛微相关安装包下载 泛微E10登录网址:https://www.e-cology.com.cn/login?service=https%3A%2F%2Fwww.e-cology.com.cn%2F Ecode使用说明:https://e-cloudstore.com/doc.html 泛微组件库:https://cloudstore.e-cology…...

软件包安装管理Gitlab

官方提供了非常详尽的系统及自动化脚本安装教程 Gitlab官网下载地址&#xff1a;https://gitlab.cn/install/ 1、安装配置 今天我们说一下包安装管理&#xff0c;这样方便我们自己更精确的制定符合我们自己需要的Gitlab仓库 配置&#xff1a;ubuntu2004(focal) 4C8G 下载程…...

在Java使用rest Client操作ES

1. 导入restClient依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.12.1</version></dependency> 2. 了解ES核心客户端API 核心区别…...

深入解析Linux软件包管理:apt/yum源配置与Vim编辑器高效使用指南

一、Linux软件包管理与开发工具 1.软件包管理器与Linux软件生态 软件包管理器的作用与分类 什么是软件包&#xff1f; 在Linux下安装软件&#xff0c;一个通常的办法是下载到程序的源代码&#xff0c;并进行编译&#xff0c;得到可执行程序。但是这样太麻烦了&#xff0c;于…...

小程序css实现容器内 数据滚动 无缝衔接 点击暂停

<view class"gundongBox"><!-- 滚动展示信息的模块 --><image class"imgWid" :src"imgurlgundong.png" mode"widthFix"></image><view class"gundongView"><view class"container&qu…...

记录 | Pycharm中如何调用Anaconda的虚拟环境

目录 前言一、步骤Step1 查看anaconda 环境名Step2 Python项目编译器更改 更新时间 前言 参考文章&#xff1a; 参考视频&#xff1a;如何在pycharm中使用Anaconda创建的python环境 自己的感想 这里使用的Pycharm 2024专业版的。我所使用的Pycharm专业版位置&#xff1a;【仅用…...

静态站点生成

以下是关于 静态站点生成(SSG) 的系统知识梳理,涵盖核心概念、核心实现、数据管理与优化等内容: 一、核心概念与优势 定义 静态站点生成(SSG)是在构建阶段预生成所有静态HTML文件的技术,用户访问时直接获取预渲染内容,无需服务器动态生成。 核心优势 性能卓越:CDN缓存…...

Android Jni(二)加载调用第三方 so 库

文章目录 Android Jni&#xff08;二&#xff09;加载调用第三方 so 库前置知识CPU架构 ABI 基本步骤1、将第三方 SO 库文件放入项目中的正确位置&#xff1a;2. 创建 JNI 接口3. 实现 JNI 层代码4、配置 CMake 常见问题解决1、UnsatisfiedLinkError&#xff1a;2、函数找不到&…...

解锁元生代:ComfyUI工作流与云原生后端的深度融合

目录 蓝耘元生代&#xff1a;智算新势力崛起​ ComfyUI 工作流创建详解​ ComfyUI 初印象​ 蓝耘平台上搭建 ComfyUI 工作流​ 构建基础工作流实操​ 代码示例与原理剖析​ 云原生后端技术全景 云原生后端概念解析​ 核心技术深度解读​ 蓝耘元生代中两者的紧密联系​…...

LeetCode算法题(Go语言实现)_47

题目 给你一个 m x n 的迷宫矩阵 maze &#xff08;下标从 0 开始&#xff09;&#xff0c;矩阵中有空格子&#xff08;用 ‘.’ 表示&#xff09;和墙&#xff08;用 ‘’ 表示&#xff09;。同时给你迷宫的入口 entrance &#xff0c;用 entrance [entrancerow, entrancecol…...

树莓派_利用Ubuntu搭建gitlab

树莓派_利用Ubuntu搭建gitlab 一、给树莓派3A搭建基本系统 1、下载系统镜像 https://cdimage.ubuntu.com/ubuntu/releases/18.04/release/ 2、准备系统SD卡 二、给树莓派设备联网 1、串口后台登录 使用串口登录后台是最便捷的&#xff0c;因为前期网络可能不好直接成功 默…...

vi(vim)编辑器和root用户与普通用户之间的转换

vim编辑器是vi编辑器的加强版&#xff0c;以vi为例&#xff1a; vi编辑器&#xff1a; vi编辑器可以编辑文件内容 如何进入vi编辑器&#xff1f; 语法&#xff1a; vi 文件路径 如何退出&#xff1f; 语法&#xff1a; wq&#xff1a;保存退出 w&#xff1a;保存 q&…...

【vscode】vscode链接关联github/gitlab

一、windows下载安装git Git - Downloading Package 二、配置Git的用户名和邮箱 Git Bash运行以下命令来配置Git的用户名和邮箱&#xff1a; git config --global user.name "你的用户名" git config --global user.email "你的邮箱地址" 生成本机秘钥…...

Redis面试问题缓存相关详解

Redis面试问题缓存相关详解 一、缓存三兄弟&#xff08;穿透、击穿、雪崩&#xff09; 1. 穿透 问题描述&#xff1a; 缓存穿透是指查询一个数据库中不存在的数据&#xff0c;由于缓存不会保存这样的数据&#xff0c;每次都会穿透到数据库&#xff0c;导致数据库压力增大。例…...

Web三漏洞学习(其一:文件上传漏洞)

靶场:云曦历年考核题 一、文件上传 在此之前先准备一个一句话木马 将其命名为muma.txt 23年秋期末考 来给师兄上个马 打开环境以后直接上传muma.txt&#xff0c;出现js弹窗&#xff0c;说明有前端验证 提示只能上传.png .jpg 和 .gif文件&#xff0c;那就把muma.txt的后缀…...

冲刺高分!挑战7天一篇nhanes机器学习SCI!DAY1-7

医学生集合啦&#xff0c;继续挑战 7天一篇nhanes机器学习SCI&#xff01; Day 1 进展&#xff1a;确定选题、期刊、文献 前面挑战了一期NHANES机器学习&#xff0c;大家使用NHANES的发文章的热情&#xff0c;火爆程度远超想象&#xff01;我在下面的评论区看到大家的学习欲…...

高并发三剑客-本地缓存之王Caffeine-01缓存应用

1 分布式缓存使用及导致的问题 1.1 hotkey典型业务场景 常规性hotkey&#xff1a;可以提前评估出hotkey的场景&#xff0c;比如&#xff1a;重要节假日、促销活动等 突发性hotkey&#xff1a;没法提前评估&#xff0c;突发性行为&#xff0c;比如&#xff1a;突然新闻、爆炸信息…...

基于Java,SpringBoot,Vue,HTML家政服务预约系统设计

摘要 本文聚焦于基于Java、SpringBoot、Vue和HTML技术的家政服务预约系统的设计与实现。该系统旨在为家政服务的供需双方搭建一个便捷、高效的在线交互平台。后端采用Java语言结合SpringBoot框架&#xff0c;充分利用SpringBoot的自动配置和快速开发特性&#xff0c;实现系统业…...

系统架构设计师:系统架构概述知识体系、考点详解、高效记忆要点、练习题并提供答案与解析

一、系统架构概述知识体系、考点详解 系统架构概述、定义与作用 1. 系统架构的定义与核心要素 系统架构是复杂系统的高层次组织结构&#xff0c;包含硬件/软件组件、交互关系、设计原则及演进策略。其核心要素包括&#xff1a; 构件与模式&#xff1a;现代架构三要素为构件…...

汽配快车道解决chrome backgroud.js(Service Worker) XMLHttpRequest is not defined问题

Chrome 扩展开发&#xff1a;Service Worker 中如何优雅地发送 HTTP 请求 在 Chrome 扩展开发中&#xff0c;Service Worker 是一个非常重要的部分&#xff0c;它可以帮助我们实现很多强大的功能。然而&#xff0c;如果你在 Service Worker 中尝试使用 XMLHttpRequest 来发送 …...

VMware Ubuntu挂载Windows机器的共享文件

https://www.dong-blog.fun/post/2029 在VMware Ubuntu中访问Windows共享文件夹&#xff1a;完整指南 在使用VMware运行Ubuntu虚拟机时&#xff0c;访问Windows主机上的文件是常见需求。本文将详细介绍如何通过网络共享方式&#xff0c;让Ubuntu虚拟机直接访问Windows主机的文…...

LeNet神经网络

一、LeNet概述 1. 历史地位 开创性模型&#xff1a;首个成功应用的卷积神经网络&#xff08;1998年&#xff09;应用场景&#xff1a;手写数字识别&#xff08;MNIST数据集&#xff09;、银行支票识别提出者&#xff1a;Yann LeCun团队&#xff08;论文《Gradient-Based Lear…...

Visio绘图工具全面科普:解锁专业图表绘制新境界[特殊字符]

Visio绘图工具全面科普&#xff1a;解锁专业图表绘制新境界&#x1f31f; 在信息爆炸的时代&#xff0c;清晰、直观地呈现复杂信息变得至关重要。无论是绘制流程图&#x1f4ca;、组织结构图&#x1f465;&#xff0c;还是规划网络拓扑&#x1f5a7;&#xff0c;一款强大的绘图…...

ECharts散点图-散点图3,附视频讲解与代码下载

引言&#xff1a; ECharts散点图是一种常见的数据可视化图表类型&#xff0c;它通过在二维坐标系或其它坐标系中绘制散乱的点来展示数据之间的关系。本文将详细介绍如何使用ECharts库实现一个散点图&#xff0c;包括图表效果预览、视频讲解及代码下载&#xff0c;让你轻松掌握…...

D3路网图技术文档

在本文档中&#xff0c;我们将探讨如何使用 D3.js&#xff0c;结合 SVG&#xff08;可缩放矢量图形&#xff09;和 Canvas&#xff0c;来实现高效、交互性强的路网图效果。D3.js 是一个强大的 JavaScript 数据可视化库&#xff0c;可以基于数据驱动文档对象模型&#xff08;DOM…...