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

Golang笔记——协程同步

大家好,这里是Good Note,关注 公主号:Goodnote,专栏文章私信限时Free。本文详细介绍Golang的协程同步的实现和应用场景。

在这里插入图片描述

文章目录

      • 协程同步是什么?
      • 为什么需要协程同步?
      • 常见的协程同步机制
        • 互斥锁(Mutex)
        • 读写锁(RWMutex)
        • 等待组(WaitGroup)
        • 通道(Channel)
        • 原子操作(Atomic Operations)
      • 典型的协程同步场景
      • 对比:`channel` 和 `sync.Mutex`, `sync.WaitGroup`
      • 互斥锁控制并发顺序
        • 使用互斥锁实现Goroutine 控制顺序
        • 解释
    • 历史文章
      • MySQL数据库
      • Redis
      • Golang

协程同步是什么?

协程同步(Goroutine synchronization)是指在多个 Goroutine 并发执行时,通过某些机制来协调它们之间的执行顺序、共享数据访问和资源管理,从而避免数据竞争(race condition)、死锁、资源冲突等问题。同步的目的是确保在并发程序中,多个 Goroutine 可以正确地共享数据、按预期的顺序执行任务,并且避免因为并发操作导致的不可预测行为。

为什么需要协程同步?

在 Go 语言中,Goroutine 是一种轻量级的线程,多个 Goroutine 可能并发执行并共享资源。在并发执行时,如果没有适当的同步机制,多个 Goroutine 可能会同时访问共享资源,导致数据不一致、程序崩溃或其他并发问题。因此,需要同步机制来确保:

  1. 共享资源的正确访问:避免多个 Goroutine 同时修改同一资源(如变量、数据结构等),从而导致数据竞态。
  2. 执行顺序的控制:确保 Goroutine 在特定顺序下执行,满足某些逻辑条件(如等待某些任务完成)。
  3. 任务完成的等待:在某些场景下,需要等待多个并发任务完成后再继续后续操作。

常见的协程同步机制

互斥锁(Mutex)

互斥锁(sync.Mutex)用于保护共享资源,确保同一时刻只有一个 Goroutine 能访问临界区(共享资源)。在锁的保护下,其他 Goroutine 必须等待,直到当前 Goroutine 完成对资源的操作并释放锁。

  • 优点

    • 简单易用,适合保护临界区。
    • 避免多个 Goroutine 同时读写共享资源时的数据竞态。
  • 缺点

    • 锁的粒度较粗,不适合高并发场景。
    • 可能会导致死锁,尤其是当锁的使用不当时。
读写锁(RWMutex)

sync.RWMutex 是一种更细粒度的锁,它允许多个 Goroutine 同时读取共享资源,但写操作时会阻止所有其他的读取和写入操作。适用于读多写少的场景。

  • 优点

    • 适合读多写少的场景,可以允许多个 Goroutine 同时读取共享数据。
    • 减少了锁竞争,提高了并发性能。
  • 缺点

    • 写操作仍然是独占的,不适用于频繁写操作的场景。
等待组(WaitGroup)

sync.WaitGroup 用于等待一组 Goroutine 完成任务。它提供了 AddDoneWait 方法,用来协调多个 Goroutine 的执行顺序。

  • 优点

    • 非常适合等待多个并发任务的完成。
    • 通过 Add 增加等待的任务数,通过 Done 表示任务完成,Wait 阻塞当前 Goroutine 直到所有任务完成。
  • 缺点

    • 只能用于同步“任务完成”,不能用于同步临界区的访问。

WaitGroup源码如下:


// A WaitGroup waits for a collection of goroutines to finish.
// The main goroutine calls Add to set the number of
// goroutines to wait for. Then each of the goroutines
// runs and calls Done when finished. At the same time,
// Wait can be used to block until all goroutines have finished.
// A WaitGroup must not be copied after first use.
type WaitGroup struct {noCopy noCopy// 64-bit value: high 32 bits are counter, low 32 bits are waiter count.// 64-bit atomic operations require 64-bit alignment, but 32-bit// compilers do not ensure it. So we allocate 12 bytes and then use// the aligned 8 bytes in them as state, and the other 4 as storage// for the sema.state1 [3]uint32
}
  • 一个WaitGroup等待多个goroutine执行完成,main的goroutine可以调用Add()方法设置需要等待的goroutine数量,之后每一个goroutine在运行结束时调用Done(),在这段时间内,我们可以使用Wait()阻塞main的goroutine直到所有的goroutine都执行完成。
  • WaitGroup不能进行复制操作【struct里面有noCopy类型,禁止做值拷贝,只能通过指针来传递】,在函数中使用 WaitGroup 时,需要传递它的指针,即 *sync.WaitGroup
通道(Channel)

Channel 是 Go 的核心特性之一,它不仅用于 Goroutine 间通信,还能通过阻塞机制隐式地实现同步。通过发送和接收操作,Channel 可以协调多个 Goroutine 的执行,确保它们按照特定顺序进行。

  • 优点

    • 通过 Channel 传递数据本身就会进行同步,使用非常灵活。
    • 可以避免使用显式的锁(如 sync.Mutex)来控制并发。
  • 缺点

    • 不适合所有场景,特别是需要复杂同步时,可能需要更多的设计。
    • 需要注意死锁和缓冲区的大小等问题。
原子操作(Atomic Operations)

sync/atomic 包提供了一些原子操作,用于在多个 Goroutine 之间同步访问单个变量。这些操作不需要使用锁,适用于简单的计数器、标志位等场景。

  • 优点

    • 对单一变量的原子操作非常高效。
    • 适用于计数器、标志位等简单的同步操作。
  • 缺点

    • 只适用于单个变量,不适合复杂的数据结构。
    • 操作较为低级,可能需要更多的代码来管理并发逻辑。

典型的协程同步场景

  1. 保护共享数据:当多个 Goroutine 需要读写共享数据时,可以使用 MutexRWMutex 来保护数据的访问。
package mainimport ("fmt""sync""time"
)func main() {var mu sync.Mutexvar counter intfor i := 0; i < 1000; i++ {go func() {mu.Lock()counter++mu.Unlock()}()}time.Sleep(time.Second * 3)fmt.Println(counter)
}

说明:time.Sleep可以使用WaitGroup进行替代。

  1. 等待多个任务完成:使用 WaitGroup 来等待多个并发任务完成后再继续执行后续操作。
package mainimport ("fmt""sync"
)func main() {var wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)go func(i int) {defer wg.Done()fmt.Println(i)}(i)}wg.Wait()
}

说明:time.Sleep可以使用WaitGroup进行替代。

  1. 协调 Goroutine 执行顺序:使用 Channel 来确保 Goroutine 按照特定顺序执行。
package mainimport ("fmt""time"
)func main() {// 创建多个无缓冲的 Channel,用来控制 Goroutine 的顺序step1 := make(chan struct{})step2 := make(chan struct{})step3 := make(chan struct{})// 定义第一个 Goroutinego func() {fmt.Println("Goroutine 1: Start")time.Sleep(1 * time.Second) // 模拟工作fmt.Println("Goroutine 1: Done")// 通知 Goroutine 2 可以开始close(step1)}()// 定义第二个 Goroutinego func() {// 等待 Goroutine 1 完成<-step1fmt.Println("Goroutine 2: Start")time.Sleep(1 * time.Second) // 模拟工作fmt.Println("Goroutine 2: Done")// 通知 Goroutine 3 可以开始close(step2)}()// 定义第三个 Goroutinego func() {// 等待 Goroutine 2 完成<-step2fmt.Println("Goroutine 3: Start")time.Sleep(1 * time.Second) // 模拟工作fmt.Println("Goroutine 3: Done")// 通知主线程结束close(step3)}()// 等待 Goroutine 3 完成<-step3fmt.Println("All Goroutines Finished!")
}
package mainimport ("fmt""time"
)func main() {// 创建多个无缓冲的 Channel,用来控制 Goroutine 的顺序step1 := make(chan struct{})step2 := make(chan struct{})step3 := make(chan struct{})// 定义第一个 Goroutinego func() {fmt.Println("Goroutine 1: Start")time.Sleep(1 * time.Second) // 模拟工作fmt.Println("Goroutine 1: Done")// 通知 Goroutine 2 可以开始step1 <- struct{}{}}()// 定义第二个 Goroutinego func() {// 等待 Goroutine 1 完成<-step1fmt.Println("Goroutine 2: Start")time.Sleep(1 * time.Second) // 模拟工作fmt.Println("Goroutine 2: Done")// 通知 Goroutine 3 可以开始step2 <- struct{}{}}()// 定义第三个 Goroutinego func() {// 等待 Goroutine 2 完成<-step2fmt.Println("Goroutine 3: Start")time.Sleep(1 * time.Second) // 模拟工作fmt.Println("Goroutine 3: Done")// 通知主线程结束step3 <- struct{}{}}()// 等待 Goroutine 3 完成<-step3fmt.Println("All Goroutines Finished!")
}

这两个代码的主要区别在于如何实现 Goroutine 之间的同步信号传递:一个使用 close() 关闭通道,另一个使用 <-chan 来发送信号。

  1. 使用 close 通道

    • close(stepX) 通知接收方,通道不再发送任何数据。这意味着接收方在收到数据后,可以认为没有更多的工作需要处理。
    • 这种方式适用于需要明确表示“结束”或“没有更多数据”的场景。
  2. 使用 <-stepX 信号传递

    • stepX <- struct{}{} 用来传递一个信号,通常是通过发送一个空的结构体。接收方通过 <-stepX 等待信号,表示前一个 Goroutine 已经完成,可以继续执行。
    • 这种方式更常见用于同步 Goroutine 之间的顺序执行,它不表示“结束”,只是简单的通知和同步。

在 Go 中,无缓冲通道chan struct{})通常用于同步信号传递。代码中使用的 step1, step2, 和 step3 是用于控制 Goroutine 执行顺序的信号通道。对于这种情形,关闭通道并不是必要的,因为:

  1. 无缓冲通道的用途:在你的代码中,通道是用于 Goroutine 之间的同步,而不是用来传输数据或产生多次通信。

  2. 通道关闭的场景:关闭通道通常用于发送者完成发送所有数据并且没有更多数据要发送时,或者用于接收方识别通道的结束。在这个例子中,主线程只需要等待第三个Goroutine 发出的信号,而不需要读取或等待更多的值。因此,不关闭通道不会导致问题

  3. 等待通道信号的场景:主线程通过 <-step3 来等待所有 Goroutine 完成。当最后一个 Goroutine 通过 step3 <- struct{}{} 完成时,主线程就能结束。无缓冲通道不需要关闭,也不会导致死锁或资源泄漏。

总结:

  • 通常在有缓冲的通道多个接收者的情况下,关闭通道的意义更大,因为接收者可能需要知道什么时候没有更多的数据,或者什么时候发送者不再发送数据。这种channel如果未关闭,可能导致它们在垃圾回收机制中未被及时回收。
  • 在当前的场景下(无缓冲通道、每个通道仅用于同步信号),没有关闭通道也不会影响程序的正确性。Go 的垃圾回收机制会自动处理那些不再使用的对象和数据结构,包括通道。所以即使没有显式关闭通道,程序结束时,未关闭的通道也会被垃圾回收。

当然,也可以在使用 <-chan 来发送信号后强制关闭通道,如下:

package mainimport ("fmt""time"
)func main() {// 创建多个无缓冲的 Channel,用来控制 Goroutine 的顺序step1 := make(chan struct{})step2 := make(chan struct{})step3 := make(chan struct{})// 定义第一个 Goroutinego func() {fmt.Println("Goroutine 1: Start")time.Sleep(1 * time.Second) // 模拟工作fmt.Println("Goroutine 1: Done")// 通知 Goroutine 2 可以开始step1 <- struct{}{}// 关闭 step1 通道,表示没有更多信号close(step1)}()// 定义第二个 Goroutinego func() {// 等待 Goroutine 1 完成<-step1fmt.Println("Goroutine 2: Start")time.Sleep(1 * time.Second) // 模拟工作fmt.Println("Goroutine 2: Done")// 通知 Goroutine 3 可以开始step2 <- struct{}{}// 关闭 step2 通道,表示没有更多信号close(step2)}()// 定义第三个 Goroutinego func() {// 等待 Goroutine 2 完成<-step2fmt.Println("Goroutine 3: Start")time.Sleep(1 * time.Second) // 模拟工作fmt.Println("Goroutine 3: Done")// 通知主线程结束step3 <- struct{}{}// 关闭 step3 通道,表示没有更多信号close(step3)}()// 等待 Goroutine 3 完成<-step3fmt.Println("All Goroutines Finished!")
}

channel同步执行Goroutine请参考:【todo】

  1. 原子操作:使用 atomic 来实现对单个变量的原子操作,如计数器的增加,但是是无序的。
package mainimport ("fmt""sync""sync/atomic"
)func main() {var count int32var wg sync.WaitGroup// 创建10个 Goroutine 来增加计数器for i := 0; i < 10; i++ {wg.Add(1)go func(i int) {defer wg.Done()// 使用 atomic 对 count 进行原子增加atomic.AddInt32(&count, 1)// 打印当前的数字fmt.Println(i)}(i)}// 等待所有 Goroutine 完成wg.Wait()// 输出最终的 count 值fmt.Println("Final count:", count)
}

对比:channelsync.Mutex, sync.WaitGroup

  • channel:更灵活,能够传递数据并同步控制执行顺序。通常用于需要显式控制执行顺序的场景,比如一个任务完成后通知另一个任务。
  • sync.Mutexsync.RWMutex:主要用于同步对共享资源的访问,无法直接控制 Goroutine 的执行顺序。
  • sync.WaitGroup:用于等待多个 Goroutine 完成,可以确保所有 Goroutine 都完成后再执行下一步,但它不控制 Goroutine 的执行顺序。

使用channel可以控制执行顺序,当然也可只使用 sync.Mutexsync.RWMutexsync.WaitGroup 来控制 Goroutine 的执行顺序,只不过没有channel那么优雅,参考下节。

互斥锁控制并发顺序

如果单独使用 sync.Mutexsync.RWMutexsync.WaitGroup 来实现 Goroutine 顺序打印 0 到 9,需要巧妙地利用 sync.Mutexsync.RWMutex 来确保 Goroutine 按顺序执行。

使用互斥锁实现Goroutine 控制顺序

我们可以通过 sync.Mutex 来实现一个基本的锁机制,确保每次只有一个 Goroutine 在执行,并按顺序打印数字。

package mainimport ("fmt""sync"
)func main() {var mu sync.Mutex// 	var mu sync.RWMutexvar wg sync.WaitGroupcounter := 0// 使用 Mutex 来控制顺序打印for i := 0; i < 10; i++ {wg.Add(1)go func(i int) {defer wg.Done()// 控制顺序打印mu.Lock()// 这里使用 counter 来确保按顺序执行for counter != i {mu.Unlock()mu.Lock()}// 打印当前数字fmt.Println(i)counter++mu.Unlock()}(i)}wg.Wait() // 等待所有 Goroutine 执行完毕
}
解释
  • 我们使用 sync.Mutex 来保护共享变量 counter,确保每个 Goroutine 在它轮到执行时才会打印。
  • counter 用于跟踪已经执行的顺序,mu.Lock()mu.Unlock() 确保只有一个 Goroutine 可以进入临界区。
  • for counter != i 的检查保证每个 Goroutine 在它的数字到达时才开始执行。

历史文章

MySQL数据库

MySQL数据库

Redis

Redis数据库笔记合集

Golang

  1. Golang笔记——语言基础知识
  2. Golang笔记——切片与数组
  3. Golang笔记——hashmap
  4. Golang笔记——rune和byte
  5. Golang笔记——channel
  6. Golang笔记——Interface类型
  7. Golang笔记——数组、Slice、Map、Channel的并发安全性

相关文章:

Golang笔记——协程同步

大家好&#xff0c;这里是Good Note&#xff0c;关注 公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍Golang的协程同步的实现和应用场景。 文章目录 协程同步是什么&#xff1f;为什么需要协程同步&#xff1f;常见的协程同步机制互斥锁&#xff0…...

1.14学习

misc buuctf-大白 由提示可以知道这个应该是修改图片的宽高了&#xff0c;下载附件后得到了图片用随波逐流直接修改图片的宽高输出即可 buuctf-乌镇峰会种图 点击下载&#xff0c;出现了一个网页为图片将图片另存为&#xff0c;用随波逐流得到的信息解不了&#xff0c;再试…...

2025 年 JavaScript 入门教程

2025 年 JavaScript 入门教程 在当今数字化时代&#xff0c;JavaScript 作为一门广泛应用于 Web 开发的编程语言&#xff0c;其重要性不言而喻。无论是前端页面的交互实现&#xff0c;还是后端服务器的逻辑处理&#xff0c;JavaScript 都发挥着关键作用。本教程旨在帮助初学者…...

paddle——站在巨人肩膀上及背刺二三事

飞桨AI Studio - 人工智能学习与实训社区 飞桨PaddlePaddle-源于产业实践的开源深度学习平台 先抛结论&#xff0c;对于想要快速了解某一领域有哪些比较适合落地的算法的从业人员来说&#xff0c;是一个很好的参考系统。从中可以知道从哪些模型里选型、如何轻量化、如何加…...

nvim , neovim , Lua 语法, text object

说明 &#xff1a; 了解一下 nvim 中的基本的 文本的类型。 基本类型有几种&#xff0c; 1 word , sentence , paragragh 2 (), {}, ,"", 3 就是 html 中的 tag 标签。 然后就是选中的类型。 1 i : 待变 inner 2 a: 代表around &#xff0c; 基本的动作有 &…...

6.2 MySQL时间和日期函数

以前我们就用过now()函数来获得系统时间&#xff0c;用datediff()函数来计算日期相差的天数。我们在计算工龄的时候&#xff0c;让两个日期相减。那么其中的这个now函数返回的就是当前的系统日期和时间。 1. 获取系统时间函数 now()函数&#xff0c;返回的这个日期和时间的格…...

批量识别图片型PDF指定区域内容识别保存表格+PDF批量改名:技术难题与项目实战总结

相关项目实战&#xff1a; 一、引言 在当今数字化办公环境中&#xff0c;批量处理PDF文件中的表格数据并进行改名是一项常见但具有挑战性的任务。无论是从大量的财务报销凭证、学术研究报告还是项目文档中提取表格信息&#xff0c;都可能遇到各种各样的技术难题。 二、批量提…...

【MySQL】索引(一)

索引 一、磁盘1、物理结构2、示意图3、定位扇区4、读写操作的基本方式 二、页1、介绍2、示例3、作用与结构4、类型&#xff08;1&#xff09;数据页&#xff08;2&#xff09;其他 5、组织与管理6、性能优化7、示意图&#xff08;B树&#xff09; 三、索引1、作用2、注意事项 四…...

缩放 对内外参的影响

当你对图像进行同比例缩小时&#xff0c;图像的内参需要相应地变化&#xff0c;但外参通常保持不变。 相机内参 相机内参&#xff08;内参矩阵&#xff09;描述了相机的固有属性&#xff0c;包括焦距和主点&#xff08;光轴与图像平面的交点&#xff09;的坐标。 当你对图像…...

excel设置好的可选择列数据后,如何快速输入到单元格中?

当设置好列的【数据】-【数据有效性】-【序列】后&#xff0c;在单元格中输入可选择数据的开头&#xff0c;就会提示出对应的可选择数据&#xff0c;然后&#xff0c;按一下键盘上的【↓】键&#xff0c;再按回车&#xff0c;即可快速输入到单元格中。...

设计一个流程来生成测试模型安全性的问题以及验证模型是否安全

要使用 Ollama 运行 llama3.3:70b 模型&#xff0c;并设计一个流程来生成测试模型安全性的问题以及验证模型是否安全&#xff0c;可以按照以下步骤进行设计和实现。整个过程包括环境配置、设计安全测试提示词、执行测试以及分析结果。以下是详细的步骤和指导&#xff1a; 1. 环…...

vue3学习日记5 - 项目起步

最近发现职场前端用的框架大多为vue&#xff0c;所以最近也跟着黑马程序员vue3的课程进行学习&#xff0c;以下是我的学习记录 视频网址&#xff1a; Day2-11.项目起步-静态资源引入和ErrorLen安装_哔哩哔哩_bilibili 学习日记&#xff1a; vue3学习日记1 - 环境搭建-CSDN博…...

ESP32,uart安装驱动uart_driver_install函数剖析,以及intr_alloc_flags 参数的意义

在 uart_driver_install 函数中&#xff0c;参数 RX_BUF_SIZE * 2 指定了接收缓冲区&#xff08;RX buffer&#xff09;的大小。这个参数对于 UART 驱动程序来说非常重要&#xff0c;因为它决定了可以存储多少接收到的数据&#xff0c;直到应用程序读取它们为止。下面是对该函数…...

android源码编译后,为什么emulator一直黑屏或者停止android界面

一、前言 最近编译了android12的源码&#xff0c;但是编译完成之后&#xff0c;emulator命令一直卡在android界面上&#xff0c;不会进入系统。经过我多方面的研究&#xff0c;终于找到解决方案。记录下来&#xff0c;希望对遇到这个问题的朋友有所帮助。 二、解决方案 网上…...

ubuntu22.04降级安装CUDA11.3

环境&#xff1a;主机x64的ubuntu22.04&#xff0c;原有CUDA12.1&#xff0c;但是现在需要CUDA11.3&#xff0c;本篇文章介绍步骤。 一、下载CUDA11.3的run文件 下载网址&#xff1a;https://developer.nvidia.com/cuda-11-3-1-download-archive?target_osLinux&target_…...

hive知识体系

hive知识体系 hive知识体系 链接: 1Hive概览 链接: 2Hive表类型 链接: 3Hive数据抽样 链接: 4Hive计算引擎 链接: 5Hive存储与压缩 链接: 6Hive Sql 大全 链接: 6Hive Sql 大全-Hive 函数 链接: 6Hive Sql 大全-窗口函数 链接: 7Hive执行计划 链接: 8Hive SQL底层执行原理 链接…...

【ASP.NET学习】Web Forms创建Web应用

文章目录 什么是 Web Forms&#xff1f;ASP.NET Web Forms - HTML 页面用 ASP.NET 编写的 Hello RUNOOB.COM它是如何工作的&#xff1f;经典 ASP ASP.NET Web Forms - 服务器控件经典 ASP 的局限性ASP.NET - 服务器控件ASP.NET - HTML 服务器控件ASP.NET - Web 服务器控件ASP.N…...

【Qt】QThread总结

目录 成员函数创建方式方式一方式二方式三注意 example总结参考文章 成员函数 创建方式 方式一 QThread 静态成员create auto thd QThread::create([]{});方式二 继承QThread类&#xff0c;重写run run函数它作为线程的入口&#xff0c;也就是线程从run()开始执行&#…...

常见的安全测试漏洞详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、SQL注入攻击 SQL 注入攻击主要是由于程序员在开发过程中没有对客户端所传输到服务器端的参数进行严格的安全检查&#xff0c;同时 SQL 语句的执行引用了该参…...

代理模式和适配器模式有什么区别

代理模式&#xff08;Proxy Pattern&#xff09;和适配器模式&#xff08;Adapter Pattern&#xff09;都是结构型设计模式&#xff0c;它们有不同的应用场景和目标&#xff0c;虽然在某些方面看起来相似&#xff0c;但它们的意图和实现方式有显著的区别。 1. 代理模式&#x…...

机器学习头歌(第三部分-强化学习)

一、强化学习及其关键元素 二、强化学习的分类 三、任务与奖赏 import numpy as np# 迷宫定义 maze np.array([[0, 0, 0, 0, 0],[0, -1, -1, 0, 0],[0, 0, 0, -1, 0],[-1, -1, 0, -1, 0],[0, 0, 0, -1, 1] ])# 定义强化学习的参数 gamma 0.8 # 折扣因子 alpha 0.5 # 学习率…...

【Hive】新增字段(column)后,旧分区无法更新数据问题

TOC 【一】问题描述 Hive修改数据表结构的需求&#xff0c;比如&#xff1a;增加一个新字段。 如果使用如下语句新增列&#xff0c;可以成功添加列col1。但如果数据表tb已经有旧的分区&#xff08;例如&#xff1a;dt20190101&#xff09;&#xff0c;则该旧分区中的col1将为…...

智能化的城市管理解决方案,智慧城管执法系统源码,微服务架构、Java编程语言、Spring Boot框架、Vue.js前端技术

智慧城管执法系统是一种高度信息化、智能化的城市管理解决方案&#xff0c;它利用现代信息技术&#xff0c;如微服务架构、Java编程语言、Spring Boot框架、Vue.js前端技术、Element UI组件库、UniApp跨平台开发工具以及MySQL数据库等&#xff0c;构建了一个综合性的执法管理平…...

【区间DP】【hard】力扣1312. 让字符串成为回文串的最少插入次数

加粗样式给你一个字符串 s &#xff0c;每一次操作你都可以在字符串的任意位置插入任意字符。 请你返回让 s 成为回文串的 最少操作次数 。 「回文串」是正读和反读都相同的字符串。 示例 1&#xff1a; 输入&#xff1a;s “zzazz” 输出&#xff1a;0 解释&#xff1a;字…...

android刷机

android ota和img包下载地址&#xff1a; https://developers.google.com/android/images?hlzh-cn android启动过程 线刷 格式&#xff1a;ota格式 模式&#xff1a;recovery 优点&#xff1a;方便、简单&#xff0c;刷机方法通用&#xff0c;不会破坏手机底层数据&#xff0…...

web-前端小实验8

实现以上图片中的内容 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"wid…...

C++实现设计模式---单例模式 (Singleton)

单例模式 (Singleton) 概念 单例模式 确保一个类在整个程序生命周期中只有一个实例&#xff0c;并提供一个全局访问点。 它是一种创建型设计模式&#xff0c;广泛用于需要共享资源的场景。 使用场景 配置管理器&#xff1a;程序中需要一个全局的配置对象。日志系统&#xff…...

【大数据】机器学习-----线性模型

一、线性模型基本形式 线性模型旨在通过线性组合输入特征来预测输出。其一般形式为&#xff1a; 其中&#xff1a; x ( x 1 , x 2 , ⋯ , x d ) \mathbf{x}(x_1,x_2,\cdots,x_d) x(x1​,x2​,⋯,xd​) 是输入特征向量&#xff0c;包含 d d d 个特征。 w ( w 1 , w 2 , ⋯ ,…...

C#类型转换

C#是静态类型的语言&#xff0c;变量一旦声明就无法重新声明或者存储其他类型的数据&#xff0c;除非进行类型转换。本章的主要任务就是学习类型转换的知识。类型转换有显式的&#xff0c;也有隐式的。所谓显式&#xff0c;就是我们必须明确地告知编译器&#xff0c;我们要把变…...

OpenCV相机标定与3D重建(55)通用解决 PnP 问题函数solvePnPGeneric()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 根据3D-2D点对应关系找到物体的姿态。 cv::solvePnPGeneric 是 OpenCV 中一个更为通用的函数&#xff0c;用于解决 PnP 问题。它能够返回多个可能…...

NVIDIA CUDA Linux 官方安装指南

本文翻译自&#xff1a;https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#post-installation-actions NVIDIA CUDALinux安装指南 CUDA工具包的Linux安装说明。 文章目录 1.导言1.1.系统要求1.2.操作系统支持政策1.3.主机编译器支持政策1.3.1.支持的C方言…...

C++中的STL

STL&#xff08;标准模板库&#xff09;在广义上分为&#xff1a;容器&#xff0c;算法&#xff0c;迭代器 容器和算法之间通过迭代器进行无缝衔接 STL大体上分为六大组件:分别为容器&#xff0c;算法&#xff0c;迭代器&#xff0c;仿函数&#xff0c;适配器&#xff0c;空间…...

前端进程和线程及介绍

前端开发中经常涉及到进程和线程的概念&#xff0c;特别是在浏览器中。理解这两个概念对于理解浏览器的工作机制和前端性能优化非常重要。以下是详细介绍&#xff1a; 1. 什么是进程和线程&#xff1f; 进程&#xff1a; 是操作系统分配资源的基本单位。一个程序启动后&#xf…...

本地用docker装mysql

目录 拉取镜像查看镜像 启动容器查看运行中的容器连接到 MySQL 容器其他一些操作 装WorkBench链接mysql——————————————允许远程登录MySql 拉取镜像 docker pull mysql查看镜像 docker image lsREPOSITORY TAG IMAGE ID CREATED SIZE mysq…...

设计模式 行为型 责任链模式(Chain of Responsibility Pattern)与 常见技术框架应用 解析

责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许将请求沿着处理者链进行发送。每个处理者对象都有机会处理该请求&#xff0c;直到某个处理者决定处理该请求为止。这种模式的主要目的是避免请求的发送者和接收者之间…...

Apache Spark中与数据分区相关的配置和运行参数

Apache Spark中与数据分区相关的配置和运行参数涉及多个方面&#xff0c;包括动态分区设置、分区数设置、Executor与并行度配置等。合理配置这些参数可以显著提高Spark作业的执行效率和资源利用率。在实际应用中&#xff0c;建议根据业务需求和计算集群的特性进行相应的调整和测…...

“深入浅出”系列之设计模式篇:(0)什么是设计模式

设计模式六大原则 1. 单一职责原则&#xff1a;一个类或者一个方法只负责一项职责&#xff0c;尽量做到类的只有一个行为原因引起变化。 核心思想&#xff1a;控制类的粒度大小&#xff0c;将对象解耦&#xff0c;提高其内聚性。 2. 开闭原则&#xff1a;对扩展开放&#xf…...

【Git版本控制器--1】Git的基本操作--本地仓库

目录 初识git 本地仓库 认识工作区、暂存区、版本库 add操作与commit操作 master文件与commit id 修改文件 版本回退 撤销修改 删除文件 初识git Git 是一个分布式版本控制系统&#xff0c;主要用于跟踪文件的更改&#xff0c;特别是在软件开发中。 为什么要版本…...

如何在Jupyter中快速切换Anaconda里不同的虚拟环境

目录 介绍 操作步骤 1. 选择环境&#xff0c;安装内核 2. 注册内核 3. 完工。 视频教程 介绍 很多网友在使用Jupyter的时候会遇到各种各样的问题&#xff0c;其中一个比较麻烦的问题就是我在Anaconda有多个Python的环境里面&#xff0c;如何让jupyter快速切换不同的Pyt…...

Python自学 - “包”的创建与使用(从头晕到了然)

<< 返回目录 1 Python自学 - “包”的创建与使用(从头晕到了然) 相对于模块&#xff0c;包是一个更大的概念&#xff0c;按照业界的开发规范&#xff0c;1个代码文件不要超过1000行&#xff0c;稍微有点规模的任务就超过这个代码限制了&#xff0c;必然需要多个文件来管…...

ElasticSearch 同义词匹配

synonym.txt 电脑, 计算机, 主机 复印纸, 打印纸, A4纸, 纸, A3 平板电脑, Pad DELETE /es_sku_index_20_20250109 PUT /es_sku_index_20_20250109 {"settings": {"index": {"number_of_shards": "5","number_of_replicas&quo…...

android 官网刷机和线刷

nexus、pixel可使用google官网线上刷机的方法。网址&#xff1a;https://flash.android.com/ 本文使用google线上刷机&#xff0c;将Android14 刷为Android12 以下是失败的线刷经历。 准备工作 下载升级包。https://developers.google.com/android/images?hlzh-cn 注意&…...

Vue环境变量配置指南:如何在开发、生产和测试中设置环境变量

-## 前言 Vue.js是一个流行的JavaScript框架&#xff0c;它提供了许多工具和功能来帮助开发人员构建高效、可维护的Web应用程序。其中一个重要的工具是环境变量&#xff0c;它可以让你在不同的环境中配置不同的参数和选项。在这篇博客中&#xff0c;我们将介绍如何在Vue应用程…...

蓝桥杯_B组_省赛_2022(用作博主自己学习)

题目链接算法11.九进制转十进制 - 蓝桥云课 进制转换 21.顺子日期 - 蓝桥云课 时间与日期 31.刷题统计 - 蓝桥云课 时间与日期 41.修剪灌木 - 蓝桥云课 思维 51.X 进制减法 - 蓝桥云课 贪心 61.统计子矩阵 - 蓝桥云课 二维前缀和 71.积木画 - 蓝桥云课 动态规划 82.扫雷 - 蓝桥…...

【干货】交换网络环路介绍

定义 以太网交换网络中为了提高网络可靠性&#xff0c;通常会采用冗余设备和冗余链路&#xff0c;然而现网中由于组网调整、配置修改、升级割接等原因&#xff0c;经常会造成数据或协议报文环形转发&#xff0c;不可避免的形成环路。如图7-1所示&#xff0c;三台设备两两相连就…...

unity——Preject3——面板基类

目录 1.Canvas Group Canvas Group 的功能 Canvas Group 的常见用途 如何使用 Canvas Group 2.代码 3.代码分析 类分析:BasePanel 功能 作用 实际应用 代码解析:hideCallBack?.Invoke(); 语法知识点 作用 虚函数(virtual)和抽象类(abstract)的作用与区别 …...

BTC系列 - 启示录

推荐《区块链启示录&#xff1a;中本聪文集》这本书, 原来早在2010年, BTC生态还不完善的时候, 社区中就已经畅想出了未来其它链上的特色方案, 中本聪也都一一做了教父级回应: coinbase币的成熟时间, 交易池, 交易确认机制, 防51%攻击, 防双重消费, 水龙头, 轻量级客户端, 链上…...

C# 25Dpoint

C# 25Dpoint &#xff0c;做一个备份 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;namespace _25Dpoint {public partial cl…...

Kotlin构造函数

class Person {var name: String? nullvar age: Int? nullfun think() {println("Mr./Ms.$name, who is $age years old, is thinking!")} }fun main () {val p Person()p.name "Jimmy"p.age 20p.think() } 在Kotlin中任意一个非抽象类都无法被继承…...

springMVC---resultful风格

目录 一、创建项目 pom.xml 二、配置文件 1.web.xml 2.spring-mvc.xml 三、图解 四、controller 一、创建项目 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi…...