深入浅出 Go 语言 sync包中的互斥锁、条件变量
深入浅出 Go 语言 sync包中的互斥锁、条件变量
引言
在并发编程中,多个 Goroutine 同时访问共享资源可能会导致数据竞争(Race Condition),进而引发程序的不一致性或崩溃。为了确保并发程序的正确性和稳定性,Go 语言提供了丰富的同步机制,帮助开发者安全地管理共享资源的访问。sync
包是 Go 语言中最常用的同步工具包,它包含了多种同步原语,如互斥锁(Mutex)、读写锁(RWMutex)、条件变量(Cond)等。
本文将深入浅出地介绍 sync
包中的主要同步原语,帮助你理解它们的工作原理和使用方法,并通过实际案例展示如何在并发程序中正确使用这些同步机制。无论你是初学者还是有经验的开发者,本文都将为你提供有价值的参考。
1. 什么是同步原语?
1.1 同步原语的概念
同步原语(Synchronization Primitives)是操作系统和编程语言中用于协调多个线程或 Goroutine 之间访问共享资源的机制。通过同步原语,你可以确保在同一时间只有一个 Goroutine 能够访问某个共享资源,从而避免数据竞争和不一致的问题。
在 Go 语言中,sync
包提供了多种同步原语,常见的包括:
- 互斥锁(Mutex):用于保护共享资源,确保同一时间只有一个 Goroutine 可以访问该资源。
- 读写锁(RWMutex):允许多个 Goroutine 同时读取共享资源,但在写操作时只允许一个 Goroutine 访问。
- 条件变量(Cond):用于在特定条件下唤醒等待的 Goroutine,常用于生产者-消费者模式。
- WaitGroup:用于等待一组 Goroutine 完成任务。
- Once:确保某个操作只执行一次。
1.2 为什么需要同步?
在并发编程中,多个 Goroutine 可能会同时访问同一个共享资源,例如全局变量、文件、数据库连接等。如果这些 Goroutine 没有进行适当的同步,可能会导致以下问题:
- 数据竞争(Race Condition):两个或多个 Goroutine 同时读写同一个变量,导致数据不一致。
- 死锁(Deadlock):多个 Goroutine 互相等待对方释放资源,导致程序无法继续执行。
- 竞态条件(Race Condition):程序的行为依赖于 Goroutine 的执行顺序,导致不可预测的结果。
为了避免这些问题,我们需要使用同步原语来协调 Goroutine 之间的访问,确保共享资源的安全性和一致性。
2. 互斥锁(Mutex)
2.1 什么是互斥锁?
互斥锁(Mutex,Mutual Exclusion Lock)是 sync
包中最基本的同步原语之一。它用于保护共享资源,确保同一时间只有一个 Goroutine 可以访问该资源。当一个 Goroutine 获取了互斥锁后,其他 Goroutine 必须等待,直到该 Goroutine 释放锁。
2.1.1 使用互斥锁
在 Go 语言中,sync.Mutex
提供了两个主要方法:
Lock()
:获取互斥锁,如果锁已被占用,则阻塞当前 Goroutine,直到锁被释放。Unlock()
:释放互斥锁,允许其他 Goroutine 获取锁。
以下是一个简单的例子,展示了如何使用互斥锁保护共享资源:
package mainimport ("fmt""sync"
)// 定义一个结构体,包含共享资源和互斥锁
type Counter struct {mu sync.Mutex // 互斥锁count int // 共享资源
}// 增加计数器的值
func (c *Counter) Increment() {c.mu.Lock() // 获取锁defer c.mu.Unlock() // 确保在函数结束时释放锁c.count++
}// 获取计数器的值
func (c *Counter) Value() int {c.mu.Lock() // 获取锁defer c.mu.Unlock() // 确保在函数结束时释放锁return c.count
}func main() {var wg sync.WaitGroupcounter := &Counter{}// 启动 1000 个 Goroutine 来增加计数器for i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()counter.Increment()}()}// 等待所有 Goroutine 完成wg.Wait()// 打印最终的计数器值fmt.Println("最终计数:", counter.Value())
}
在这个例子中,我们定义了一个 Counter
结构体,包含一个共享资源 count
和一个互斥锁 mu
。通过 Increment()
和 Value()
方法,我们可以安全地增加和获取计数器的值。Lock()
和 Unlock()
方法用于确保同一时间只有一个 Goroutine 可以访问 count
,从而避免数据竞争。
2.2 互斥锁的最佳实践
- 尽量减少锁的持有时间:长时间持有锁会影响程序的性能,因此应尽量减少锁的持有时间。可以通过将锁的范围限制在最小的代码块内来实现这一点。
- 避免嵌套锁:如果多个 Goroutine 需要获取多个锁,可能会导致死锁。因此,应尽量避免嵌套锁,或者确保锁的获取顺序一致。
- 使用
defer
释放锁:在获取锁后,务必确保在函数结束时释放锁。可以使用defer
关键字来确保即使发生错误或异常,锁也会被正确释放。
3. 读写锁(RWMutex)
3.1 什么是读写锁?
读写锁(RWMutex,Read-Write Mutex)是 sync
包中的一种更灵活的锁机制。与互斥锁不同,读写锁允许多个 Goroutine 同时读取共享资源,但在写操作时只允许一个 Goroutine 访问。这使得读写锁在读多写少的场景下具有更好的性能。
3.1.1 使用读写锁
sync.RWMutex
提供了三个主要方法:
RLock()
:获取读锁,允许多个 Goroutine 同时读取共享资源。RUnlock()
:释放读锁。Lock()
:获取写锁,确保同一时间只有一个 Goroutine 可以写入共享资源。Unlock()
:释放写锁。
以下是一个简单的例子,展示了如何使用读写锁保护共享资源:
package mainimport ("fmt""sync""time"
)// 定义一个结构体,包含共享资源和读写锁
type Cache struct {mu sync.RWMutex // 读写锁data map[string]string
}// 设置缓存数据
func (c *Cache) Set(key, value string) {c.mu.Lock() // 获取写锁defer c.mu.Unlock() // 确保在函数结束时释放锁c.data[key] = value
}// 获取缓存数据
func (c *Cache) Get(key string) string {c.mu.RLock() // 获取读锁defer c.mu.RUnlock() // 确保在函数结束时释放锁if value, ok := c.data[key]; ok {return value}return ""
}func main() {cache := &Cache{data: make(map[string]string)}// 启动多个 Goroutine 来读取和写入缓存var wg sync.WaitGroup// 写入数据wg.Add(1)go func() {defer wg.Done()cache.Set("key1", "value1")time.Sleep(time.Second) // 模拟写操作的时间}()// 读取数据for i := 0; i < 5; i++ {wg.Add(1)go func(i int) {defer wg.Done()value := cache.Get("key1")fmt.Printf("Goroutine %d 获取到的值: %s\n", i, value)}(i)}// 等待所有 Goroutine 完成wg.Wait()
}
在这个例子中,我们定义了一个 Cache
结构体,包含一个共享资源 data
和一个读写锁 mu
。通过 Set()
和 Get()
方法,我们可以安全地写入和读取缓存数据。RLock()
和 RUnlock()
方法用于获取和释放读锁,允许多个 Goroutine 同时读取缓存;Lock()
和 Unlock()
方法用于获取和释放写锁,确保同一时间只有一个 Goroutine 可以写入缓存。
3.2 读写锁的最佳实践
- 读多写少的场景使用读写锁:如果读操作远远多于写操作,使用读写锁可以显著提高性能,因为多个 Goroutine 可以同时读取共享资源。
- 避免长时间持有写锁:长时间持有写锁会影响其他 Goroutine 的读取操作,因此应尽量减少写锁的持有时间。
- 确保读锁和写锁的正确配合:在读写锁的使用中,必须确保读锁和写锁的正确配合,避免出现死锁或数据竞争。
4. 条件变量(Cond)
4.1 什么是条件变量?
条件变量(Cond,Condition Variable)是 sync
包中用于在特定条件下唤醒等待的 Goroutine 的机制。条件变量通常与互斥锁一起使用,允许 Goroutine 在满足某些条件时继续执行,而在条件不满足时进入等待状态。
4.1.1 使用条件变量
sync.Cond
提供了以下方法:
NewCond(lock)
:创建一个新的条件变量,参数lock
是一个互斥锁,用于保护共享资源。Wait()
:使当前 Goroutine 进入等待状态,直到其他 Goroutine 调用Signal()
或Broadcast()
唤醒它。Signal()
:唤醒一个正在等待的 Goroutine。Broadcast()
:唤醒所有正在等待的 Goroutine。
以下是一个简单的例子,展示了如何使用条件变量实现生产者-消费者模式:
package mainimport ("fmt""sync"
)// 定义一个缓冲区,用于存储生产者生成的数据
type Buffer struct {mu sync.Mutexcond *sync.Condbuffer []intcapacity int
}// 初始化缓冲区
func NewBuffer(capacity int) *Buffer {b := &Buffer{buffer: make([]int, 0, capacity),capacity: capacity,}b.cond = sync.NewCond(&b.mu)return b
}// 生产者向缓冲区添加数据
func (b *Buffer) Produce(data int) {b.mu.Lock()for len(b.buffer) == b.capacity {b.cond.Wait() // 缓冲区已满,等待消费者消费}b.buffer = append(b.buffer, data)fmt.Printf("生产者添加数据: %d\n", data)b.cond.Signal() // 唤醒一个等待的消费者b.mu.Unlock()
}// 消费者从缓冲区获取数据
func (b *Buffer) Consume() int {b.mu.Lock()for len(b.buffer) == 0 {b.cond.Wait() // 缓冲区为空,等待生产者生产}data := b.buffer[0]b.buffer = b.buffer[1:]fmt.Printf("消费者获取数据: %d\n", data)b.cond.Signal() // 唤醒一个等待的生产者b.mu.Unlock()return data
}func main() {buffer := NewBuffer(3)// 启动生产者 Goroutinego func() {for i := 0; i < 5; i++ {buffer.Produce(i)}}()// 启动消费者 Goroutinego func() {for i := 0; i < 5; i++ {buffer.Consume()}}()// 等待一段时间,确保所有 Goroutine 完成time.Sleep(time.Second * 2)
}
在这个例子中,我们定义了一个 Buffer
结构体,用于模拟生产者-消费者模式中的缓冲区。生产者通过 Produce()
方法向缓冲区添加数据,消费者通过 Consume()
方法从缓冲区获取数据。sync.Cond
用于在缓冲区满或空时让生产者或消费者进入等待状态,直到条件满足时被唤醒。
4.2 条件变量的最佳实践
- 条件变量必须与互斥锁一起使用:条件变量依赖于互斥锁来保护共享资源,因此必须确保在调用
Wait()
、Signal()
和Broadcast()
时已经获取了相应的锁。 - 避免频繁唤醒:
Signal()
和Broadcast()
会唤醒等待的 Goroutine,但唤醒过多的 Goroutine 可能会导致性能下降。因此,应尽量减少不必要的唤醒操作。 - 使用
for
循环检查条件:在调用Wait()
之前,建议使用for
循环检查条件是否满足,以避免虚假唤醒(Spurious Wakeup)问题。
5. WaitGroup
5.1 什么是 WaitGroup?
WaitGroup 是 sync
包中用于等待一组 Goroutine 完成任务的同步机制。WaitGroup
通过计数器来跟踪 Goroutine 的完成情况,当计数器为零时,表示所有 Goroutine 已经完成。
5.1.1 使用 WaitGroup
sync.WaitGroup
提供了以下方法:
Add(delta)
:增加或减少计数器的值,通常在启动 Goroutine 之前调用。Done()
:减少计数器的值,通常在 Goroutine 完成任务时调用。Wait()
:阻塞当前 Goroutine,直到计数器为零。
以下是一个简单的例子,展示了如何使用 WaitGroup
等待多个 Goroutine 完成任务:
package mainimport ("fmt""sync""time"
)func worker(id int, wg *sync.WaitGroup) {defer wg.Done() // 任务完成后减少计数器fmt.Printf("Worker %d 开始工作的\n", id)time.Sleep(time.Second) // 模拟工作时间fmt.Printf("Worker %d 完成工作的\n", id)
}func main() {var wg sync.WaitGroup// 启动 5 个 Goroutinefor i := 1; i <= 5; i++ {wg.Add(1) // 增加计数器go worker(i, &wg)}// 等待所有 Goroutine 完成wg.Wait()fmt.Println("所有任务已完成")
}
在这个例子中,我们使用 WaitGroup
来等待 5 个 Goroutine 完成任务。每个 Goroutine 在开始时调用 wg.Add(1)
增加计数器,在完成任务时调用 wg.Done()
减少计数器。主 Goroutine 通过 wg.Wait()
阻塞,直到所有子 Goroutine 完成任务。
5.2 WaitGroup 的最佳实践
- 确保
Add()
和Done()
成对使用:Add()
和Done()
必须成对使用,否则可能会导致计数器不匹配,导致程序无法正常结束。 - 避免在 Goroutine 外部调用
Done()
:Done()
应该在 Goroutine 内部调用,确保只有在 Goroutine 完成任务后才会减少计数器。 - 使用
defer
调用Done()
:在 Goroutine 中使用defer
调用Done()
,确保即使发生错误或异常,计数器也会被正确减少。
6. Once
6.1 什么是 Once?
Once 是 sync
包中用于确保某个操作只执行一次的同步机制。sync.Once
通过内部的状态标志来确保即使多个 Goroutine 同时调用 Do()
方法,也只会有一个 Goroutine 执行指定的操作。
6.1.1 使用 Once
sync.Once
提供了以下方法:
Do(f func())
:执行一次指定的操作f
,如果该操作已经执行过,则不会再次执行。
以下是一个简单的例子,展示了如何使用 Once
确保某个操作只执行一次:
package mainimport ("fmt""sync"
)var once sync.Once
var value intfunc initialize() {fmt.Println("初始化操作...")value = 42
}func main() {// 启动多个 Goroutine 来调用 initialize()var wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)go func() {defer wg.Done()once.Do(initialize)fmt.Printf("值: %d\n", value)}()}// 等待所有 Goroutine 完成wg.Wait()
}
在这个例子中,我们使用 sync.Once
来确保 initialize()
只会被调用一次,即使多个 Goroutine 同时调用 once.Do(initialize)
。sync.Once
通过内部的状态标志来保证这一点,确保即使多个 Goroutine 同时尝试执行 initialize()
,也只会有一个 Goroutine 实际执行该操作。
6.2 Once 的最佳实践
- 确保操作的幂等性:
sync.Once
保证操作只执行一次,但不能保证操作本身是幂等的。因此,应确保Do()
中的操作是幂等的,即多次执行不会产生不同的结果。 - 避免在
Do()
中使用复杂的逻辑:Do()
中的操作应该是简单且快速的,避免在其中执行耗时的操作,以免影响程序的性能。
7. 总结
通过本文的学习,你已经掌握了 sync
包中常用的同步原语,包括互斥锁、读写锁、条件变量、WaitGroup 和 Once。这些同步机制能够帮助你在并发编程中安全地管理共享资源,避免数据竞争和不一致的问题。无论是保护共享变量、实现生产者-消费者模式,还是确保某个操作只执行一次,sync
包都为你提供了强大的支持。
参考资料
参考资料
- Go 官方文档 - sync 包
- Go 语言中文网 - 并发编程与 sync 包
相关文章:
深入浅出 Go 语言 sync包中的互斥锁、条件变量
深入浅出 Go 语言 sync包中的互斥锁、条件变量 引言 在并发编程中,多个 Goroutine 同时访问共享资源可能会导致数据竞争(Race Condition),进而引发程序的不一致性或崩溃。为了确保并发程序的正确性和稳定性,Go 语言提…...
利用Python实现子域名简单收集
免责申明 本文仅是用于学习研究子域名信息收集,请勿用在非法途径上,若将其用于非法目的,所造成的一切后果由您自行承担,产生的一切风险和后果与笔者无关;本文开始前请认真详细学习《中华人民共和国网络安全法》【学法…...
npm发布插件到私有仓库保姆级教程
在开发项目的过程中,我们经常需要安装插件依赖,那么怎么把自己开发的组件封装成一个插件,并发布到npm 插件市场或者上传到私有仓库里面呢?今天总结下自己发布插件到私有仓库的记录: 一、创建组件 执行命令创建一个空…...
利用Java easyExcel库实现高效Excel数据处理
在Java应用程序中,处理Excel文件是一项常见任务,尤其是在需要读取、写入或分析大量数据时。easyExcel是一个基于Java的高性能Excel处理库,它提供了简洁的API和优化的性能,以简化Excel文件的处理。本文将指导您如何使用easyExcel库…...
基于Springboot的校园交友网站设计与实现
1.1 管理信息系统概述 管理信息系统是计算机在信息管理领域的一种实用技术。通过运用管理科学、数学和计算机应用的原理及方法,在符合软件工程规范的原则下,形成一套完整的理论和方法体系。是一个以人、计算机和其他外部设备组成的可以进行信息的收集、…...
android studio 读写文件操作(应用场景三)
android studio版本:2023.3.1 patch2 例程:filesaveandread 其实我写这个都是我记录我要做后个数独小游戏,每一个都是为了解决一个问题。即是分享也是备忘,反正我什么都不会,就是一顿瞎改,不行就研究。这…...
小程序 —— Day1
组件 — view和scroll-view view 类似于HTML中的div,是一个块级元素 案例:通过view组件实现页面的基础布局 scroll-view 可滚动的视图区域,用来实现滚动列表效果 案例:实现纵向滚动效果 scroll-x属性:允许横向滚动…...
使用 PyTorch 和 Horovod 来编写一个简单的分布式训练 demo
使用 PyTorch 和 Horovod 来编写一个简单的分布式训练 demo,可以帮助你理解如何在多GPU或多节点环境中高效地训练深度学习模型。Horovod 是 Uber 开发的一个用于分布式训练的框架,它支持 TensorFlow、Keras、PyTorch 等多个机器学习库。下面是一个基于 P…...
【Linux】以 CentOS 为例备份与恢复/home分区,并调整分区容量
在 Linux 系统中,这里举例对 /home 目录进行备份、重建和恢复操作,并调整分区大小、更换文件系统或修复损坏的分区等。 〇、前提条件 确认文件系统类型为 xfs。 确认 /home 目录确实没有重要数据,或者已经做好了数据备份。 确保在执行这些…...
OpenAI 12Days 第二天 强化微调(RFT):推动语言模型在科学研究中的应用
OpenAI 12Days 第二天 强化微调(RFT):推动语言模型在科学研究中的应用 文章目录 OpenAI 12Days 第二天 强化微调(RFT):推动语言模型在科学研究中的应用RFT的工作原理与应用领域案例研究:基因突变…...
神经网络的梯度反向传播计算过程,举例说明
目录 神经网络的梯度反向传播计算过程 网络结构 权重和偏置 激活函数 前向传播 损失函数 反向传播 参数更新 举例 神经网络的梯度反向传播计算过程 为了说明神经网络的梯度反向传播计算过程,我们考虑一个简单的全连接网络,该网络有一个输入层、一个隐藏层和一个输出…...
定点数乘法:补码一位算法(booth算法)
方法 初始化 将被乘数A放在寄存器A中。 将乘数B放在寄存器B中,并在最低位添加一个额外的位Q(-1) 0。 结果寄存器P初始化为0,长度为2n位。 迭代过程(重复n次) 对于i从0到n-1: 检查乘数B的最后两位(B 和…...
robots.txt
robots.txt 文件是网站管理者用来告知搜索引擎爬虫(也称为机器人或蜘蛛)哪些页面可以抓取,哪些页面不应该被抓取的一种文本文件。它位于网站的根目录下,并且文件名必须全部小写。这个文件对于SEO(搜索引擎优化…...
如何用 JavaScript 操作 DOM 元素?
如何用 JavaScript 操作 DOM 元素?——结合实际项目代码示例讲解 在前端开发中,DOM(文档对象模型)操作是与页面交互的核心。通过 DOM 操作,开发者可以动态地修改页面内容、响应用户交互、控制样式等。JavaScript 提供…...
vue3使用后端传递的文件流进行文件预览
文章目录 一、 注意事项1、responseType设置为:arraybuffer2、Blob设置type,来源于后台封装的response.headers[content-type]3、使用encodeURIComponent(),避免符号影响文件名 二、java接口 一、 注意事项 1、responseType设置为࿱…...
ubuntu20.04设置远程桌面
安装xrdp sudo apt install xrdp 2、 检查xrdp状态 sudo systemctl status xrdp3、(若为Ubuntu 20)添加xrdp至ssl-cert sudo adduser xrdp ssl-cert 4、重启服务 sudo systemctl restart xrdp最后可以远程了,注意一个账号只能一个登录...
在vue3里使用scss实现简单的换肤功能
实现的换肤功能:主题色切换、亮色模式和暗黑模式切换、背景图切换 主题色就是网站主色,可以配置到组件库上面;亮色模式又分为两种风格:纯白风格和背景图风格,不需要背景图的话可以删掉这部分逻辑和相关定义;…...
flyway执行sql遇到变量执行报错解决
前两天在公司使用flyway工具执行sql时,开发写的sql里面有变量,于是这个flyway工具不识别这个变量直接报错,不接着往下执行了。报错信息如下: flyway工具执行sql报错 information: No value provided for placeholder: ${ep1} 于是…...
解谜类游戏《迷失岛2》等如何抽象出一套通用高效开发框架?
解谜类游戏以精妙的谜题设计和引人入胜的故事叙述为特点,考验着玩家的智慧与观察力。《迷失岛2》与《南瓜先生2九龙城寨》正是这一领域的佳作。游戏以独特的艺术风格和玩法设计吸引了大量玩家,而它们背后隐藏着一套强大的框架。 上海胖布丁游戏的技术总…...
Ant Design Vue v4版本如何解决1px没有被postcss-px2rem转成rem的问题
背景说明 如果你的 Ant Design Vue 项目有要做适配的需求,那首先要选择一种适配方案。笔者选择的是用 postcss-px2rem 进行适配。笔者在配置了 postcss-px2rem的相关配置后,发现 postcss-px2rem 没有对 Ant Design Vue 进行适配。在网上看了一些文章之后…...
【系统架构设计师论文】云上自动化运维及其应用
随着云计算技术的迅猛发展,企业对云资源的需求日益增长。为了应对这一挑战,云上自动化运维(CloudOps)应运而生,它结合了DevOps理念和技术,通过自动化工具和流程来提高云环境的管理效率和服务质量。本文将探讨云上自动化运维的主要衡量指标,并详细介绍一个实际项目中如何…...
河南地质灾害资质办理的政策
一、资质分类 资质等级: 甲级资质:由自然资源部审批管理,适用于承担大型地质灾害防治项目。 乙级资质:由省、自治区、直辖市自然资源主管部门审批管理,适用于承担中型地质灾害防治项目。 丙级资质:由省…...
单例模式--懒汉 饿汉模式
一.啥是单例模式? 先介绍一下设计模式: 设计模式好⽐象棋中的 "棋谱". 红⽅当头炮, ⿊⽅⻢来跳. 针对红⽅的⼀些⾛法, ⿊⽅应招的时候有⼀些固定的套路. 按照套路来⾛局势就不会吃亏. 软件开发中也有很多常⻅的 "问题场景". 针对这些问题…...
HDD 2025年技术趋势深度分析报告
随着数据量的指数级增长以及人工智能(AI)、物联网(IoT)、云计算和视频监控等领域的需求激增,硬盘驱动器(HDD)行业正面临着前所未有的挑战与机遇。本报告旨在深入剖析2025年HDD技术的发展方向&am…...
关于uni-app的uni.showModal和indexOf的使用
这里使用showModal时,这个里面的content需要使用到字符串的形式,不能用到number类型 uni.showModal({title: 提示,content: "hello",success: function (res) {if (res.confirm) {console.log(用户点击确定);} else if (res.cancel) {console…...
Spring Data Elasticsearch
简介说明 spring-data-elasticsearch是比较好用的一个elasticsearch客户端,本文介绍如何使用它来操作ES。本文使用spring-boot-starter-data-elasticsearch,它内部会引入spring-data-elasticsearch。 Spring Data ElasticSearch有下边这几种方法操作El…...
汇编语言简要记录-1
汇编语言与汇编指令 汇编语言的主题是汇编指令 汇编指令与机器指令的差别在于指令的表示方法上 1、汇编指令是机器机器指令便于记忆的书写格式 2、汇编指令是机器指令的助记符 ag:机器指令 1000100111011000操作:将寄存器BX的值送到AX中汇编指令 MOV …...
Java程序猿搬砖笔记(十七)
文章目录 MySQL触发器ElasticSearch按日期分组查询每天的文档数量MySQL中order by排序将null排在最前或者最后面swagger3.0默认访问路径swagger3.0模块化配置MySQL中要少用UNION,多用UNION ALLElasticSearch Bucket & Metric聚合分析及嵌套聚合Mysql case when做…...
代码设计:设计模式:观察者模式
文章目录 定义类结构应用总结 定义 实现响应式编程的代码设计,即触发事件或数据变化时,将数据从被观察者类通过观察器传递给观察者处理,即被观察者类间接调用观察者类的方法处理事件或数据 类结构 被观察者类、观察器类、观察者类 被观察…...
第32天:安全开发-JavaEE应用Servlet路由技术JDBCMybatis数据库生命周期
时间轴: 32天主要学习内容: 1、JavaEE-HTTP-Servlet技术 2、JavaEE-数据库-JDBC&Mybatis java技术使用历史(2023 ): JavaEE-HTTP-Servlet&路由&周期: java学习范围: 3、Java: 功能:数据…...
如何使用Apache HttpClient来执行GET、POST、PUT和DELETE请求
Apache HttpClient 是一个功能强大且灵活的库,用于在Java中处理HTTP请求。 它支持多种HTTP方法,包括GET、POST、PUT和DELETE等。 本教程将演示如何使用Apache HttpClient来执行GET、POST、PUT和DELETE请求。 Maven依赖 要使用Apache HttpClient&…...
Next.js 系统性教学:加载界面、重定向与路由分组
更多有关Next.js教程,请查阅: 【目录】Next.js 独立开发系列教程-CSDN博客 目录 1. 加载界面与流式渲染 1.1 加载界面 (loading.js) 1.2 流式渲染 2. 路由重定向 2.1 基于服务器的重定向 2.2 动态重定向 2.3 中间件中的重定向 3. 路由分组 3.1…...
哪款云手机适合多开?常用云手机功能对比
在全球化和数字化时代,云手机以其独特的灵活性和高效性,成为多账号运营和数字营销的热门工具。云手机能够解决传统设备管理的诸多痛点,例如账号关联、硬件成本高等问题。本文将为您推荐多款优质云手机品牌,帮助您选择最适合的工具…...
基于openzeppelin插件的智能合约升级
一、作用以及优点 部署可升级合约,插件自动部署proxy和proxyAdmin合约,帮助管理合约升级和交互;升级已部署合约,通过插件快速升级合约,脚本开发方便快捷;管理代理管理员的权限,只有proxyAdmin的…...
WGAN生成对抗网络数据生成
数据生成 | WGAN生成对抗网络数据生成 目录 数据生成 | WGAN生成对抗网络数据生成生成效果基本描述程序设计参考资料 生成效果 基本描述 1.WGAN生成对抗网络,数据生成,样本生成程序,MATLAB程序; 2.适用于MATLAB 2020版及以上版本&…...
SQL面试题——拼多多SQL面试题 求连续段的起始位置和结束位置
拼多多SQL面试题 求连续段的起始位置和结束位置 今天的题目来自拼多多,我们先看一下题目描述 有一张表ids记录了id,id不重复,但是会存在间断,求出连续段的开始位置和结束位置 +---+ | id| +---+ | 1| | 2| | 3| | 5| | 6| | 8| | 10| | 12| | 13| | 14| | 15| +--…...
Contextual Affinity Distillation for Image Anomaly Detection
Contextual Affinity Distillation for Image Anomaly Detection 日本东北大学 摘要 先前对无监督工业异常检测的研究主要通过匹配或学习局部特征表示来关注“结构”类型的异常,例如裂纹和颜色污染。虽然在这种异常上实现了显着的高检测性能,但他们面…...
如何在HTML中修改光标的位置(全面版)
如何在HTML中修改光标的位置(全面版) 在Web开发中,控制光标位置是一个重要的技巧,尤其是在表单处理、富文本编辑器开发或格式化输入的场景中。HTML中的光标位置操作不仅适用于表单元素(如<input>和<textarea…...
Spring Cloud Alibaba(六)
目录: 分布式链路追踪-SkyWalking为什么需要链路追踪什么是SkyWalkingSkyWalking核心概念什么是探针Java AgentJava探针日志监控实现之环境搭建Java探针日志监控实现之探针实现编写探针类TestAgent搭建 ElasticsearchSkyWalking服务环境搭建搭建微服务微服务接入Sky…...
Http请求系列---【http的几个请求时间分别代表什么?以及如何设置?】
在HTTP客户端编程中,通常涉及以下几种关键的超时设置: 连接超时 (connectTimeout): 定义:在与服务器建立连接时等待的最大时间。这包括DNS解析时间、连接建立时间等。作用:如果在指定的时间内无法建立连接,…...
如何将CSDN博客下载为PDF文件
1.打开CSDN文章内容 2.按键盘上的f12键(或者右键—审查元素)进入浏览器调试模式,点击控制台(Console)进入控制台 3.在控制台输入以下代码,回车 4.在弹出的打印页面中将布局设置成横向,纵向会…...
关于IDEA 2024.2.1 Java EE 无框架配置Tomcat环境以及servlet使用教程
前言 这里的IDEA使用的是专业版,大学生认证后即可使用,社区版没有接触过暂不提,如果你是社区版,那么很可惜,本博客并不适用。本博客适用于java web刚入门的朋友学习使用,并不适用于高级部署。注意…...
【23种设计模式】七种设计原则:理论与 Java 实践
文章目录 23 种设计模式之七种设计原则:理论与 Java 实践一、单一职责原则(SRP - Single Responsibility Principle)(一)理论介绍(二)Java 实现示例(三)关键步骤…...
数据库与数据库管理系统概述
title: 数据库与数据库管理系统概述 date: 2024/12/7 updated: 2024/12/7 author: cmdragon excerpt: 在信息化迅速发展的时代,数据已成为企业和组织的重要资产。数据库与数据库管理系统(DBMS)是高效存储、管理和利用数据的核心工具。本文首先定义了数据库的基本概念和特…...
42_GAN网络详解(2)---常见的GAN
DCGAN CGAN 条件生成对抗网络(Conditional Generative Adversarial Networks, CGAN)是生成对抗网络(Generative Adversarial Networks, GAN)的一种变体,由Mehdi Mirza和Simon Osindero在2014年提出。CGAN的主要改进在…...
目前国内【齿轮检测仪】行业整体较为分散,行业竞争日趋激烈
摘要 根据 HengCe (恒策咨询)的统计及预测,2023年全球齿轮检测仪市场销售额达到了6.2亿美元,预计2030年将达到9.4亿美元,年复合增长率(CAGR)为6.0%(2024-2030)。地区层面来看&#…...
【学习路线】Java
Java基础 基础 基础语法 面向对象 集合框架 JCF 进阶 并发编程 JVM 企业级开发 框架 Spring Boot Spring Cloud 分布式 高性能 高可用 安全 基建 Docker 实战 数据库 MySQL Redis 计算机基础 计算机组成原理 操作系统 计算机网络 数据结构与算法 设计模式 参考:…...
一文说清flink从编码到部署上线
引言:目前flink的文章比较多、杂,很少有一个文章,从一个简单的例子入手,说清楚从编码、构建、部署全流程是怎么样的。所以编写本文,自己做个记录备查同时跟大家分享一下。本文以简单的mysql cdc为例展开说明。 环境说明…...
dolphinScheduler 任务调度
#Using docker-compose to Start Server #下载:https://dlcdn.apache.org/dolphinscheduler/3.1.9/apache-dolphinscheduler-3.1.9-src.tar.gz $ DOLPHINSCHEDULER_VERSION3.1.9 $ tar -zxf apache-dolphinscheduler-"${DOLPHINSCHEDULER_VERSION}"-src.t…...
【opencv入门教程】14. 矩阵乘除运算
文章选自: 一、函数multiply、divide //乘法 CV_EXPORTS_W void multiply(InputArray src1, InputArray src2,OutputArray dst, double scale 1, int dtype -1); brief 计算两个数组的每个元素的按比例缩放乘积 note 当输出数组的深度为 CV_32S 时,…...