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

【Go 基础】并发相关

并发相关

CAS

CAS算法(Compare And Swap),是原⼦操作的⼀种,,CAS 算法是⼀种有名的⽆锁算法。⽆锁编程,即不使⽤锁的情况下实现多线程之间的变量同步。可⽤于在多线程编程中实现不被打断的数据交换操作,从⽽避免多线程同时改写某⼀数据时由于执⾏顺序不确定性以及中断的不可预知性产⽣的数据不⼀致问题。

Go中的CAS操作是借⽤了CPU提供的原⼦性指令来实现。 CAS操作修改共享变量时候不需要对共享变量加锁,⽽是通过类似乐观锁的⽅式进⾏检查,本质还是不断的占⽤CPU 资源换取加锁带来的开销(⽐如上下⽂切换销)。

package mainimport ("fmt""sync""sync/atomic"
)var (counter int32          //计数器wg      sync.WaitGroup //信号量
)func main() {threadNum := 5wg.Add(threadNum)for i := 0; i < threadNum; i++ {go incCounter(i)}wg.Wait()
}
func incCounter(index int) {defer wg.Done()spinNum := 0for {// 原⼦操作old := counterok := atomic.CompareAndSwapInt32(&counter, old, old+1)if ok {break} else {spinNum++}}fmt.Printf("thread,%d,spinnum,%d\n", index, spinNum)
}

当主函数 main ⾸先创建了5个信号量,然后开启五个线程执⾏ incCounter ⽅法,incCounter 内部执⾏, 使⽤ CAS 操作递增 counter 的值,atomic.CompareAndSwapInt32 具有三个参数,第⼀个是变量的地址,第⼆个是变量当前值,第三个是要修改变量为多少,该函数如果发现传递的 old 值等于当前变量的值,则使⽤第三个变量替换变量的值并返回 true,否则返回 false。

这⾥之所以使⽤⽆限循环是因为在⾼并发下每个线程执⾏ CAS 并不是每次都成功,失败了的线程需要重写获取变量当前的值,然后重新执⾏ CAS 操作。

go 中 CAS 操作可以有效的减少使⽤锁所带来的开销,但是需要注意在⾼并发下这是使⽤ cpu 资源做交换的。

什么是并发编程

首先,要明确并行和并发的概念。

并行,是指两个或者多个事件在同一时刻发生。并发,是指两个或多个事件在同一时间间隔发生。

并发偏重于多个任务交替执行,而多个任务之间还可能是串行的。而并行才是真正意义上的“同时执行”。

**并发编程是指在一台机器上“同时”处理多个任务。**并发是在同一实体上的多个事件,多个事件在同一时间间隔发生。并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

go 并发机制及 CSP 并发模型

CSP 模型

CSP 模型是上个世纪七十年代提出的,不同于传统的多线程通过共享内存来通信,CSP 讲究的是“以通信的方式来共享内存”。用于描述两个独立的并发实体通过共享的通讯 channel 进行通信的并发模型。CSP 中的 channel 是第一类对象,它不关注发送消息的实体,而是关注发送消息时使用的 channel。

go 中 channel 一般创建后,用于协程之间传递信息,类似于一个消息队列。

go 并发机制

goroutine 是 go 实际并发执行的实体,它底层使用协程实现并发,协程是一种运行在用户态的用户线程,类似于 greenthread,go 底层选择使用协程的原因如下:

  • 它在用户空间操作,避免了内核态和用户态的切换成本
  • 可以由语言和框架层进行调度
  • 相较于线程,拥有更小的栈空间,允许创建大量的协程
goroutine 的特性

go 中有三个对象:

  • G(goroutine):我们说的协程,是用户级的轻量级线程,每个 goroutine 对象中的 sched 保存着它的上下文信息
  • M(work thread):对 OS 内核级线程的封装,数量对应真实的 CPU 数量(一般远大于)
  • P(processor):逻辑处理器,即为 G 和 M 的调度对象,用来调度 G 和 M 之间的关联关系,数量可以通过 GOMAXPROCS() 来设置,默认为核心数

正常情况下,一个 CPU 对象启动一个 M,M 检查并执行 G。遇到 G 阻塞的时候,会启动一个新的 M,以充分利用 CPU 资源。因此,有时候 M 数量会远大于 CPU个数。

GPM 如何调度
  1. 新创建的 G 会先保存在 P 的本地队列中,如果本地队列已满,那么就会保存在全局的 G 队列中,等待被 P 执行。
  2. M 和 P 绑定之后,M 会不断从 P 的本地队列中无锁地取出 G,并切换到这个 G 的堆栈执行。当 P 的本地 G 队列空了,就会从全局 G 队列获取一批 G。当全局队列中也没有待运行的 G 时,就会尝试从其他的 P 窃取部分 G 来执行,相当于 P 之间的负载均衡。
一轮调度

其实,一般我们知道上面的调度逻辑就差不多了。不过,如果需要深入了解的话,还是从最开始的一轮调度开始理一下吧。

引导程序会为 Go 程序的运行建立必要的环境,在完成一系列初始化工作之后,Go 程序的 main 函数才会真正执行。引导程序会在最后让调度器进行一轮调度,这样才能让封装了 main 函数的 G 能马上执行。

一轮调度的总体流程

  1. **调度器先判断当前 M 是否被锁定。**一般来说,Go 的调度器会按照一定策略动态地关联 M、P 和 G,并以此高效的执行并发程序,一般来说并不需要用户程序的任何干预。但是在极少情况下,用户程序不得已需要对 Go 的运行时调度进行干预。

    **锁定 M 和 G 可以说是为了 CGO 准备的。**因为有些 C 语言的函数库(如 OpenGL)会用到线程本地存储技术,将数据存储在当前内核线程的私有缓存中。因此,包含了调用此类 C 函数库的代码的 G 会变得特殊:**在特定时间只能与同一个 M 产生关联,这样会对调度效率造成负面影响。**从上面的流程图可以看出,就算锁定了,一定要尽量减少锁定的时间。

  2. 如果发现当前 M 已经和某个 G 锁定了,那么会立即停止调度并停止当前 M。一旦和它锁定的 G 处于可运行状态,它就会被唤醒并继续运行这个 G。

  3. 如果判断当前 M 未与任何 G 锁定,那么调度器会检查是否有运行时串行任务正在等待执行(此类任务执行时需要停止 Go 调度器,STW)。如果 gcwaiting 不为 0,那么会停止并阻塞当前 M 以等待运行时串行任务执行完成。

  4. 否则,开始真正的可运行 G 寻找之旅。一旦找到一个可运行的 G,调度器会在判定该 G 未与任何 M 锁定之后,立即让当前 M 运行它。(查找的过程:先尝试获取执行 GC 任务的 G -> 从全局可运行 G 队列获取 G -> 从本地 P 得可运行 G 队列获取 G,最后到全力查找可运行的 G)

全力查找可运行的 G

概括来说,就是会多次尝试从各个地方查找可运行的 G:

  1. **获取执行终结器的 G:**所有的终结函数的执行都会由一个专用的 G 负责,如果这个专用 G 已完成任务,调度器就会获取它,把它置为 Grunable 状态并放入本地 P 的可运行 G 队列
  2. **从本地 P 得可运行 G 队列获取 G:**调度器尝试从这里获取一个 G,并返回
  3. **从调度器的可运行 G 队列获取 G:**调度器尝试从这里获取一个 G,并返回
  4. **从网络 I/O 轮询器获取 G:**如果 netpoller 已经被初始化且有过网络 I/O 操作,那么调度器就会尝试从这里获取一个 G 列表,并且把表头的 G 返回,同时把其他的 G 都放入调度器的可运行 G 队列。如果没有初始化或者没有过网络 I/O 操作,就跳过。这里没有获取成功也不会阻塞。
  5. **从其他 P 的可运行 G 队列获取 G:**条件允许时,调度器会使用伪随机算法从全局 P 列表中选取 P,然后尝试从它们的可运行 G 队列中盗取一半的 G 到本地 P 的可运行 G 队列。选取 P 和盗取 G 会重复多次,成功就停止。如果一直没有成功,那么第一阶段结束。
  6. **获取执行 GC 标记任务的 G:**第二阶段,调度器会先判断是否正处于 GC 的标记阶段,以及本地 P 是否可以用于 GC 标记任务,如果都是 true,就将本地 P 持有的 GC 标记专用 G 置为 Grunnable 并返回
  7. 从调度器的可运行 G 队列获取 G:调度器再次尝试从这里获取一个 G,并返回。如果还是找不到,就会解除本地 P 与当前 M 地关联,并把该 P 放入调度器的空闲 P 列表
  8. **从全局 P 列表中的每个 P 得可运行 G 队列获取 G:**遍历全局 P 中的所有 P,检查他们的可运行 G 队列。只要某个 P 的可运行 G 队列不为空,那就从调度器的空闲 P 列表中取一个 P,判定其可用后,将其和当前 M 关联在一起,然后返回第一阶段(第五步),否则继续后续;
  9. **获取执行 GC 标记任务的 G:**判断是否正处于 GC 的标记阶段,以及与 GC 标记任务相关的全局资源是否可用,如果都是 true,调度器从空闲 P 列表拿出一个 P。如果这个 P 持有一个 GC 标记专用 G,就关联该 P 与当前 M,然后再次执行第二阶段(从6 开始)
  10. **从网络 I/O 轮询器获取 G:**此步骤与步骤4 基本相同,但是这里的获取是阻塞的。只有当 netpoller 那里有可用的 G 时,阻塞才会解除。同样的,如果没有初始化或者没有过网络 I/O 操作,就跳过。

如果经过上述10个步骤之后,还没有找到可运行的 G,调度器就会停止当前 M。后续重新唤醒时,会重新进入查找可运行的 G 的子流程。

自旋状态: 意味着它还没有找到 G 来运行。一般来说,运行时系统中至少会有一个自旋的 M,调度器也会尽量保证有一个自旋的 M 存在。除非发现没有自旋的 M,调度器是不会新启用或恢复一个 M 去运行新 G 的。

为什么要有 P
  • GM 模型的问题
    • 存在单一的全局 mutex 和集中状态管理:mutex 需要保护所有与 goroutine 相关的操作,导致锁竞争严重;
    • goroutine 交接问题:M 之间经常交接可运行的 goroutine,而且可能会导致延迟增加和额外开销。每个 M 必须能够执行任何可运行的 G;
    • 每个 M 都需要做内存缓存:会导致资源消耗过大,数据局部性差
    • 存在系统调用的情况下,线程经常会被阻塞和解阻塞。这增加了很多额外的性能开销
  • GMP 模型的优化
    • 每个 P 都有自己的本地队列,大幅度减轻了对全局队列的直接依赖,所带来的效果就是锁竞争的减少,而 GM 模型的性能开销大头就是锁竞争;
    • 每个 P 相对的平衡上,在 GMP 模型中也实现了 Work Stealing 算法,如果 P 的本地队列为空,会从全局队列或者其他 P 得本地队列窃取可运行的 G 来运行,减少空转,提高了资源利用率;

一般来说,M 的数量都会多于 P。在 Go 中,M 的数量默认是 10000,P 的默认数量是 CPU 核数。由于 M 的属性,也就是如果存在系统阻塞调用,阻塞了 M,又不够用的情况下,M 会不断增加。

M 不断增加的话,如果本地队列挂载在 M 上,那么就意味着本地队列也会随之增加。这显然是不合理的,因为本地队列的管理会变得复杂,且 Work Stealing 性能会大幅度下降。

M 被系统调用阻塞之后,我们是期望他能将未执行的任务分配给其他 M 继续运行,而不是全部停止。因此,使用 M 是不合理的,引入新的组件 P,把本地队列关联到 P 上,就能很好的解决这个问题。

go 的 CSP 并发模型

go 的 CSP 并发模型,是通过 goroutine 和 channel 来实现的。

goroutine 是 go 中并发的执行单位,channel 用于各个并发结构体之间的通信。

go 中常用的并发模型

通过 channel 通知实现并发控制

无缓冲的通道指的是通道的大小为 0,也就是说,这种类型的通道在接收前没有能力保存任何值,它要求发送 goroutine 和接收 goroutine 同时准备好,才可以完成发送和接收操作。无缓冲的通道我们也称之为同步通道,通过它接可以简单实现并发控制。

func main() {ch := make(chan struct{})go func() {fmt.Println("start working")time.Sleep(time.Second * 1)ch <- struct{}{}}()<- chfmt.Println("finished")
}
通过 sync 包中的 WaitGroup 实现并发控制

goroutine 是异步执行的,有的时候为了防止在结束 main 函数的时候结束掉 goroutine,所以需要同步等待,这时候就需要使用 WaitGroup 了。在 Sync 包中,提供了 WaitGroup,它会等待它收集的所有 goroutine 任务全部完成。

需要注意的是,WaitGroup 在第一次使用后,不能被拷贝。

func main() {wg := sync.WaitGroup{}for i := 0; i < 5; i++ {wg.Add(1)go func(wg sync.WaitGroup, i int) {fmt.Printf("i:%d", i)wg.Done()}(wg, i)}wg.Wait()fmt.Println("exit")
}

上面代码运行之后会出现死锁。是因为 wg 给拷贝传递到了 goroutine 中,导致只有 Add 操作,其实 Done 操作是在 wg 的副本执行的。因此,Wait 就会死锁。

有两个修改方式:1️⃣ 将匿名函数中的 wg 传入类型改为 *sync.WaitGroup,这样就能引用到正确的 WaitGroup 了。2️⃣ 将匿名函数中的 wg 的的传入参数去掉,因为 go 支持闭包类型,在匿名函数中可以直接使用外面的 wg 变量。

Go 1.7 之后的 Context

通常,一些简单场景下使用 channel 和 WaitGroup 已经足够了,但是当面临一些复杂多变的网络并发场景下 channel 和 WaitGroup 就显得有些力不从心了。

⽐如⼀个⽹络请求 Request,每个 Request 都需要开启⼀个 goroutine 做⼀些事情,这些 goroutine ⼜可能会开启其他的 goroutine,⽐如数据库和RPC服务。

所以我们需要⼀种可以跟踪 goroutine 的⽅案,才可以达到控制他们的⽬的,这就是 Go 语⾔为我们提供的 Context,称之为上下⽂⾮常贴切,它就是 goroutine 的上下⽂。

它是包括⼀个程序的运⾏环境、现场和快照等。每个程序要运⾏时,都需要知道当前程序的运⾏状态,通常Go 将这些封装在⼀个 Context ⾥,再将它传给要执⾏的 goroutine 。context 包主要是⽤来处理多个 goroutine 之间共享数据,及多个 goroutine 的管理。

context 包的核⼼是 struct Context,接⼝声明如下:

// A Context carries a deadline, cancelation signal, and requestscoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {// Done returns a channel that is closed when this `Context` is canceled// or times out.// Done() 返回⼀个只能接受数据的channel类型,当该context关闭或者超时时间到了的时候,该channel就会有⼀个取消信号Done() <-chan struct{}// Err indicates why this Context was canceled, after the Done channel// is closed.// Err() 在Done() 之后,返回context 取消的原因。Err() error// Deadline returns the time when this Context will be canceled, if any.// Deadline() 设置该context cancel的时间点Deadline() (deadline time.Time, ok bool)// Value returns the value associated with key or nil if none.// Value() ⽅法允许 Context 对象携带request作⽤域的数据,该数据必须是线程安全的。Value(key interface{}) interface{}
}

Context 对象是协程安全的,你可以把⼀个 Context 对象传递给任意个数的 gorotuine,对它执⾏取消操作时,所有 goroutine 都会接收到取消信号。

⼀个 Context 不能拥有 Cancel ⽅法,同时我们也只能 Done channel 接收数据。其中的原因是⼀致的:接收取消信号的函数和发送信号的函数通常不是⼀个。

典型的场景是:⽗操作为⼦操作操作启动 goroutine,⼦操作也就不能取消⽗操作。

相关文章:

【Go 基础】并发相关

并发相关 CAS CAS算法&#xff08;Compare And Swap&#xff09;&#xff0c;是原⼦操作的⼀种,&#xff0c;CAS 算法是⼀种有名的⽆锁算法。⽆锁编程&#xff0c;即不使⽤锁的情况下实现多线程之间的变量同步。可⽤于在多线程编程中实现不被打断的数据交换操作&#xff0c;从…...

5G CPE终端功能及性能评测(四)

5G CPE 功能性能评测 本文选取了几款在工业应用领域应用较多的5G CPE,对其功能和性能进行了对比评测。功能方面主要对比了网络接口数量,VPN功能 支持情况。以下测试为空口测试,测试结果受环境影响较大,性能仅供参考。总体看,高通X55芯片下行最优,速率稳定。 功能 对比CPE…...

开源模型应用落地-qwen模型小试-Qwen2.5-7B-Instruct-tool usage入门-集成心知天气(二)

一、前言 Qwen-Agent 是一个利用开源语言模型Qwen的工具使用、规划和记忆功能的框架。其模块化设计允许开发人员创建具有特定功能的定制代理,为各种应用程序提供了坚实的基础。同时,开发者可以利用 Qwen-Agent 的原子组件构建智能代理,以理解和响应用户查询。 本篇将介绍如何…...

JS进阶DAY3|事件(一)事件监听及事件类型

目录 一、事件监听方式&#xff08;绑定&#xff09; 1.1 DOM0级事件 1.2 DOM2级事件 1.3 区别 二、事件类型 2.1 鼠标事件 2.2 键盘事件 2.3 焦点事件 2.4 表单事件 2.5 加载和卸载事件 2.6 滚动事件 2.7 触摸事件&#xff08;在支持触摸的设备上&#xff09; 一…...

数据结构与算法之美:单链表

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《数据结构与算法之美》、《编程之路》、《题海拾贝》 欢迎点赞&#xff0c;关注&#xff01; 目录 …...

从ctfwiki开始的pwn之旅 6.ret2reg

原理 Ret2reg&#xff0c;即攻击绕过地址混淆(ASLR)&#xff0c;返回到寄存器地址 一般用于开启ASLR的ret2shellcode题型。 出现该漏洞的代码的典型特征是&#xff1a;程序中存在strcpy的字符串拷贝函数。 ret2reg&#xff08;返回到寄存器地址攻击&#xff09;是一种利用技…...

GESP202412 7级 T2

闲话 花了一个小时。 主要原因&#xff1a;条初始值硬控我半小时&#xff0c;题目看错硬控我半小时&#xff08;悲&#xff09;。 正文 看题目&#xff0c;就是求从哪个点出发所得到的所有单调下降序列的总长度最长(这个描述好奇怪&#xff0c;不过意思是对的)。 题目中说…...

LeetCode---426周赛

题目列表 3370. 仅含置位位的最小整数 3371. 识别数组中的最大异常值 3372. 连接两棵树后最大目标节点数目 I 3373. 连接两棵树后最大目标节点数目 II 一、仅含置位位的最小整数 题目要求我们返回二进制数位全为1&#xff0c;且大于 n 的最小的整数&#xff0c;我们可以直…...

git 拉取代码时报错 gitignore Please move or remove them before you merge.

git 拉取代码时报错&#xff0c; The following untracked working tree files would be overwritten by merge: .gitignore Please move or remove them before you merge. 当你在使用 Git 进行代码拉取&#xff08;通常是执行 git pull 或 git merge 命令&#xff09;时遇到这…...

devops-Dockerfile+Jenkinsfile方式部署Java前后端应用

文章目录 概述部署前端Vue应用一、环境准备1、Dockerfile2、.dockerignore3、nginx.conf4、Jenkinsfile 二、Jenkins部署1、新建任务2、流水线3、Build Now 构建 & 访问 Springboot后端应用1. 准备工作2. 创建项目结构3. 编写 Dockerfile后端 Dockerfile (backend/Dockerfi…...

华为云鸿蒙应用入门级开发者认证考试题库(理论题和实验题)

注意&#xff1a;考试链接地址&#xff1a;华为云鸿蒙应用入门级学习认证_华为云鸿蒙应用入门级开发者认证_华为云开发者学堂-华为云 当前认证打折之后是1元&#xff0c;之后原价700元&#xff0c;大家尽快考试&#xff01;考试题库里面答案不一定全对&#xff0c;但是可以保证…...

Lua元表和元方法的使用

元表是一个普通的 Lua 表&#xff0c;包含一组元方法&#xff0c;这些元方法与 Lua 中的事件相关联。事件发生在 Lua 执行某些操作时&#xff0c;例如加法、字符串连接、比较等。元方法是普通的 Lua 函数&#xff0c;在特定事件发生时被调用。 元表包含了以下元方法&#xff1…...

自动驾驶算法——卡尔曼滤波器平滑感知车道线参数【C++代码实现】

1.算法原理 在工程实践中,由于感知识别到的车道线偶尔存在较大的跳变,导致后端控制算法计算出的控制角度也存在较大的跳变,所以我们需要对感知输入的车道线系数进行平滑处理。 已知卡尔曼滤波算法主要分为以下几大步骤: 感知将车道线以三次螺旋曲线方程 y = c 0 + c 1 x +…...

浅谈Scala语言

一、Scala语言基础 1.1 语法结构 Scala的语法是一种融合了多种编程范式的设计&#xff0c;它结合了函数式编程和面向对象编程的优点。这种语法结构使得Scala既具有高度的表达能力&#xff0c;又保持了代码的简洁性。例如&#xff0c;Scala支持类型推断&#xff0c;这意味着在…...

可视化建模以及UML期末复习篇----UML图

这是一篇相对较长的文章&#xff0c;如你们所见&#xff0c;比较详细&#xff0c;全长两万字。我不建议你们一次性看完&#xff0c;直接跳目录找你需要的知识点即可。 --------欢迎各位来到我UML国&#xff01; 一、UML图 总共有如下几种&#xff1a; 用例图&#xff08;Use Ca…...

音视频入门基础:MPEG2-TS专题(13)——FFmpeg源码中,解析Section Header的实现

一、引言 在《音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;11&#xff09;—— TS中的Section》中讲述了Section Header的基本概念&#xff0c;本文讲述FFmpeg源码中是怎样解析Section Header的。 二、parse_section_header函数的定义 FFmpeg源码中通过parse_section…...

SQL UCASE() 函数:转换字符串为大写

SQL UCASE() 函数&#xff1a;转换字符串为大写 概述 在SQL中&#xff0c;UCASE() 函数用于将字符串中的所有字符转换为大写。这是一个非常实用的函数&#xff0c;尤其在处理大量文本数据时&#xff0c;确保数据的一致性和准确性。本文将详细介绍UCASE() 函数的用法、示例以及…...

机器学习周报(12.2-12.8)

文章目录 摘要Abstract Vision Transformer1 原理2 代码 摘要 本周学习了Vision Transformer (ViT) 的基本原理及其实现&#xff0c;并完成了基于PyTorch的模型训练、验证和预测任务。深入理解了ViT如何将图像分割成patch作为输入序列&#xff0c;并结合Transformer Encoder处…...

【机器人】系统辨识之激励轨迹设计(傅里叶级数)

在机器人参数辨识中使用傅里叶级数&#xff0c;主要是为了生成一种激励轨迹&#xff0c;具有良好的数学特性&#xff0c;同时符合物理要求。傅里叶级数的特性使其在机器人动力学辨识的激励轨迹设计中非常常用。 1. 为什么需要激励轨迹&#xff1f; 激励轨迹的作用是通过驱动机…...

JVM八股文精简

目录 简述JVM类加载过程简述JVM中的类加载器简述双亲委派机制双亲委派机制的优点简述JVM内存模型简述程序计数器简述虚拟机栈简述本地方法栈简述JVM中的堆简述方法区简述运行时常量池简述Java创建对象的过程简述JVM给对象分配内存的策略Java对象内存分配是如何保证线程安全的如…...

PyTorch基本使用——张量的索引操作

在操作张量时&#xff0c;经常要去获取某些元素进行处理或者修改操作&#xff0c;在这里需要了解torch中的索引操作。 准备数据&#xff1a; data torch.randint(0,10,[4,5]) print(data--->,data)输出结果&#xff1a; data---> tensor([[3, 9, 4, 0, 5],[7, 5, 9, …...

mysql集群MHA方式部署

1. 基本信息 部署机器角色部署路径192.168.242.71MySQL-Mater MHA-NodeMySQL: /alidata1/mysql-8.0.28192.168.242.72MySQL-Slave MHA-NodeMHA-Node: /alidata1/admin/tools/mha4mysql-node-0.58192.168.242.73MySQL-Slave MHA-Node192.168.242.74MHA-ManagerMHA-Manager: …...

Spring Boot中实现JPA多数据源配置指南

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;本文详细介绍了在Spring Boot项目中配置和使用JPA进行多数据源管理的步骤。从引入依赖开始&#xff0c;到配置数据源、创建DataSource bean、定义实体和Repository&#xff0c;最后到配置事务管理器和使用多数据…...

【计算机网络】实验12:网际控制报文协议ICMP的应用

实验12 网际控制报文协议ICMP的应用 一、实验目的 验证ping命令和tracert命令的工作原理。 二、实验环境 Cisco Packet Tracer模拟器 三、实验过程 1.构建网络拓扑并进行信息标注&#xff0c;将所需要配置的IP地址写在对应的主机或者路由器旁边&#xff0c;如图1所示。 图…...

MVC基础——市场管理系统(二)

文章目录 项目地址三、Produtcts的CRUD3.1 Products列表的展示页面(Read)3.1.1 给Product的Model里添加Category的属性3.1.2 View视图里展示Product List3.2 增加Product数据(Add)3.2.1 创建ViewModel用来组合多个Model3.2.2 在_ViewImposts里引入ViewModels3.2.3 添加Add的…...

使用Redis的Bitmap实现签到功能

1.基础签到实现 1.1代码如下Service Slf4j public class SignInService {Autowiredprivate StringRedisTemplate redisTemplate;private static final String SIGN_KEY_PREFIX "sign:";/*** 用户签到* param userId 用户ID* param date 签到日期*/public boolean s…...

OpenCV 图像基本操作

OpenCV快速通关 第一章&#xff1a;OpenCV 简介与环境搭建 第二章&#xff1a;OpenCV 图像基本操作 OpenCV 图像基本操作 OpenCV快速通关第二章&#xff1a;OpenCV 图像基本操作一、相关结构体与函数介绍&#xff08;一&#xff09;cv::Mat 结构体&#xff08;二&#xff09;c…...

SpringBoot3+Micormeter监控应用指标

监控内容简介 SpringBoot3项目监控服务 &#xff0c;可以使用Micormeter度量指标库&#xff0c;帮助我们监控应用程序的度量指标&#xff0c;并将其发送到Prometheus中并用Grafana展示。监控指标有系统负载、内存使用情况、应用程序的响应时间、吞吐量、错误率等。 micromete…...

Leetcode打卡:变为棋盘

执行结果&#xff1a;通过 题目&#xff1a;782 变为棋盘 一个 n x n 的二维网络 board 仅由 0 和 1 组成 。每次移动&#xff0c;你能交换任意两列或是两行的位置。 返回 将这个矩阵变为 “棋盘” 所需的最小移动次数 。如果不存在可行的变换&#xff0c;输出 -1。 “棋盘…...

遣其欲,而心自静 -- 33DAI

显然&#xff0c;死做枚举只能的50分。 错了4次总算对了。 大体思路&#xff1a; 因题目说只有两个因数&#xff0c;那么有两种情况&#xff1a; 1&#xff1a;两个质数相乘&#xff0c;如&#xff1a;3*515 5*745 等&#xff08;不包括5*525 或5*315 重复计算\ 因为3*5算了…...

物品识别 树莓派 5 YOLO v5 v8 v10 11 计算机视觉

0. 要实现的效果 让树莓派可以识别身边的一些物品&#xff0c;比如电脑&#xff0c;鼠标&#xff0c;键盘&#xff0c;杯子&#xff0c;行李箱&#xff0c;双肩包&#xff0c;床&#xff0c;椅子等 1. 硬件设备 树莓派 5 raspberrypi.com/products/raspberry-pi-5/树莓派官方摄…...

鸿蒙NEXT元服务:静态卡片

【引言】 最近上线的鸿蒙NEXT元服务受到了一些用户的反馈&#xff0c;指出其缺乏一个直观的入口。为了解决这个问题并提供类似传统应用程序的桌面快捷方式体验&#xff0c;决定通过添加静态卡片功能来让用户能够直接从桌面访问元服务。本文将详细介绍如何实现这一功能。 【参考…...

vue3父子组件通信

一般常用有6种方式: 使用 props 传递数据:父组件通过 props 传递数据给子组件,子组件通过 defineProps 获取父组件定义的数据。使用 v-model 语法糖:父组件通过 v-model="abc" 传递数据,并监听子组件的更新事件,子组件:通过 defineEmits 获取父传入的属性的更…...

2024年认证杯SPSSPRO杯数学建模D题(第一阶段)AI绘画带来的挑战解题全过程文档及程序

2024年认证杯SPSSPRO杯数学建模 D题 AI绘画带来的挑战 原题再现&#xff1a; 2023 年开年&#xff0c;ChatGPT 作为一款聊天型AI工具&#xff0c;成为了超越疫情的热门词条&#xff1b;而在AI的另一个分支——绘图领域&#xff0c;一款名为Midjourney&#xff08;MJ&#xff…...

虚幻引擎---材质篇

一、基础知识 虚幻引擎中的材质&#xff08;Materials&#xff09; 定义了场景中对象的表面属性&#xff0c;包括颜色、金属度、粗糙度、透明度等等&#xff1b;可以在材质编辑器中可视化地创建和编辑材质&#xff1b;虚幻引擎的渲染管线的着色器是用高级着色语言&#xff08;…...

【Linux基础】yum 与 vim 的操作

目录 Linux 应用商店——yum yum和yum源是什么 关于镜像的简单理解 yum 的基本操作 yum的安装 yum install 命令 yum查看软件包 yum list 命令 yum的卸载 yum remove 命令 关于 rzsz 软件 安装 rzsz 软件&#xff1a; rz 命令 sz 命令 yum 源拓展 Linux 编辑器…...

一句话木马

作用&#xff1a; 一句话木马的作用主要是利用计算机系统或网络协议的安全漏洞&#xff0c;以实现未经授权访问、数据窃取或其他恶意目的。 木马举例&#xff1a; 1.PHP <?php eval($_POST[attack]);?> 解释 • <?php ... ?> 是 PHP 代码的开始和结束标记…...

给建筑物“穿毛衣”:AI绘图新玩法

随着气温的骤降&#xff0c;我们不仅感受到了自然界的寒冷&#xff0c;甚至连城市的建筑物似乎也在寒风中“颤抖”。在这样的背景下&#xff0c;一种新颖的AI绘图玩法——给建筑“穿毛衣”在网络上迅速走红。本文将详细介绍这一创意玩法&#xff0c;并提供手把手的教学指导。 A…...

【Qt】Qt Creator项目文件(.pro 文件)构建指令学习

文章目录 1. DESTDIR作用&#xff1a;实例&#xff1a; 2. INCLUDEPATH作用&#xff1a;实例&#xff1a; 3. LIBS作用&#xff1a;用法&#xff1a;实例&#xff1a; 4. TEMPLATE作用&#xff1a;实例&#xff1a; 5. OTHER_FILES作用&#xff1a;实例&#xff1a;其它说明 6.…...

突破!自然语言强化学习(NLRL):一个可处理语言反馈的强化学习框架

本论文由伦敦大学学院、上海交通大学、布朗大学、布里斯托大学、新加坡国立大学以及萨里大学的研究者合作完成。 冯熙栋是论文第一作者&#xff0c;即将毕业于伦敦大学学院。目前是Google DeepMind的Research Scientist&#xff0c;主要研究方向包括强化学习与生成模型。刘博是…...

core Webapi jwt 认证

core cookie 验证 Web API Jwt 》》》》用户信息 namespace WebAPI001.Coms {public class Account{public string UserName { get; set; }public string UserPassword { get; set; }public string UserRole { get; set; }} }》》》获取jwt类 using Microsoft.AspNetCore.Mvc…...

【Springboot知识】springboot基础-事件

文章目录 简介一、事件类型二、事件处理机制三、自定义事件和监听器四、异步事件处理五、条件事件监听 如何使用1. 自定义事件2. 发布事件3. 监听事件4. 测试事件机制 ApplicationEventPublisher接口使用的设计模式 简介 在Spring Boot中&#xff0c;事件机制是一种基于观察者…...

经典视觉神经网络1 CNN

一、概述 输入的图像都很大&#xff0c;使用全连接网络的话&#xff0c;计算的代价较高&#xff0c;图像也很难保留原本特征。 卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;是一种专门用于处理具有网格状结构数据的深度学习模型。主要应用…...

解决跨域问题方案

跨域问题在前后端分离架构下尤为常见&#xff0c;是每个 Web 开发者都会遇到的核心问题。本文将通过原理解析、场景剖析、解决方案详解以及最佳实践等多个维度&#xff0c;帮助开发者全面理解并有效应对跨域问题。 目录 **一、跨域的本质****1. 同源策略****2. 同源策略的限制范…...

【python自动化五】接口自动化基础--requests的使用

python的接口请求可以用requests库&#xff0c;这个介绍就不多说了&#xff0c;网上说得很详细。 接下来直接记录下如何使用&#xff08;当然也不限于自动化的使用&#xff09; 1.安装requests requests也需要安装一下 pip install requests2.requests请求 1.常用的请求方法…...

文本三剑客——grep命令

介绍 作用 Linux grep (global regular expression) 命令用于查找文件里符合条件的字符串或正则表达式。 常用选项 -i&#xff1a;忽略大小写进行匹配。-v&#xff1a;反向查找&#xff0c;只打印不匹配的行。-n&#xff1a;显示匹配行的行号。-r&#xff1a;递归查找子目录…...

ASP 实例:深入解析与实战应用

ASP 实例&#xff1a;深入解析与实战应用 引言 ASP&#xff08;Active Server Pages&#xff09;是一种由微软开发的服务器端脚本环境&#xff0c;用于动态网页设计和开发。它允许开发者创建和运行动态交互性网页&#xff0c;如访问数据库、发送电子邮件等。本文将深入探讨AS…...

文件的操作

什么是文件 如何是数据持久化——保存在硬盘上(文件&#xff0c;数据库)磁盘上的文件是文件在程序设计中&#xff0c;我们一般谈的文件有两种&#xff1a;程序文件、数据文件程序文件&#xff0c;比如源文件&#xff08;.c文件&#xff09;读a文件写到b文件里&#xff0c;此时a…...

【简单谈谈UCIE PHY LSM链路训练】

UCIE PHY LSM链路训练 1 UCIE PHY LSM1.1 RESET1.2 SBINT1.3 MBINT1.3.1 MBINT.PARAM1.3.2 MBINIT.CAL1.3.3 MBINIT.REPAIRCLK1.3.4 MBINIT.REPAIRVAL1.3.5 MBINIT.REVERSALMB1.3.6 MBINIT.REPAIRMB 1.4 MBTRAIN1.5 LINKINIT1.6 ACTIVE1.7 L1/L21.8 PHY.RETRAIN1.9 TRAIN.ERROR…...

数学二常用公式(高等数学+线性代数)

目录 高等数学第一章 函数、极限和连续第二章 一元函数微分学第三章 一元函数积分学第四章 多元函数微分学第五章 多元函数积分学第六章 常微分方程 线性代数线性代数篇章涉及的知识内容及常用公式第一章 行列式第二章 矩阵第三章 向量第四章 线性方程组第五章 矩阵的相似化简第…...