Go:goroutine 和通道
goroutine
f() // 等待 f() 返回
go f() // 新建一个调用 f() 的 goroutine,不用等待
在 Go 语言里,goroutine 是并发执行的活动单元。与顺序执行程序不同,在有多个 goroutine 的并发程序中,不同函数可同时执行。程序启动时,首个调用main
函数的 goroutine 为主 goroutine,新的 goroutine 通过go
语句创建,go
语句在函数或方法调用前加上go
关键字,且go
语句本身执行立即完成,不等待函数执行结束。
package mainimport ("fmt""time"
)func main() {go sinner(100 * time.Microsecond)const n = 45fibN := fib(n)fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN)
}func sinner(delay time.Duration) {for {for _, r := range `-\|/` {fmt.Printf("\r%c", r)time.Sleep(delay)}}
}func fib(x int) int {if x < 2 {return x}return fib(x - 1) + fib(x - 2)
}
示例:主 goroutine 计算第 45 个斐波那契数(使用低效递归算法,耗时较长),同时启动另一个 goroutine 运行spinner
函数,spinner
函数不断打印旋转字符提示程序在运行。当fib(45)
计算完成,main
函数输出结果后返回,此时所有 goroutine 强制终结,程序退出。除程序正常返回或退出外,无法从外部直接停止一个 goroutine,但可通过通信让其自行停止。程序中两个并发活动(指示器显示和斐波那契数计算)相互独立运行 。
示例:并发时钟服务器
顺序时钟服务器
func main() {listener, err := net.Listen("tcp", "localhost:8000")if err != nil {log.Fatal(err)}for {conn, err := listener.Accept()if err != nil {log.Print(err)continue}handleConn(conn)}
}func handleConn(conn net.Conn) {defer conn.Close()for {_, err := io.WriteString(conn, time.Now().Format("15:04:05\n"))if err != nil {return}time.Sleep(1 * time.Second)}
}
- 实现原理:使用
net.Listen
创建net.Listener
在localhost:8000
监听 TCP 连接,Accept
方法阻塞等待连接,接收到连接后由handleConn
函数处理。handleConn
函数在循环中,每秒通过time.Now
获取当前时间,利用net.Conn
满足io.Writer
接口的特性,将时间格式化后写入连接发送给客户端。若写入失败(如客户端断开连接),则关闭连接,继续等待下一个连接请求。
func main() {conn, err := net.Dial("tcp", "localhost:8000")if err != nil {log.Fatal(err)}defer conn.Close()mustCopy(os.Stdout, conn)
}func mustCopy(dst io.Writer, src io.Reader) {if _, err := io.Copy(dst, src); err != nil {log.Fatal(err)}
}
- 客户端连接:客户端可使用
telnet
,nc
(netcat
)工具或自定义的基于net.Dial
实现的 Go 版netcat
程序连接服务器。顺序服务器一次只能处理一个客户端请求,第二个客户端需等第一个结束才能正常工作。
并发时钟服务器
func main() {listener, err := net.Listen("tcp", "localhost:8000")if err != nil {log.Fatal(err)}for {conn, err := listener.Accept()if err != nil {log.Print(err)continue}go handleConn(conn)}
}
在顺序时钟服务器基础上,只需在调用handleConn
函数处添加go
关键字,使其在新的 goroutine 内执行,就能让服务器并发处理多个客户端连接,多个客户端可同时接收到时间信息。
示例:并发回声服务器
简单回声服务器实现
func handleConn(c net.Conn) {io.Copy(c, c) // 注意: 忽略错误c.Close()
}
普通回声服务器可通过handleConn
函数,利用io.Copy
将读取到的内容写回客户端,之后关闭连接,但此版本忽略错误处理。
模拟真实回声的回声服务器
func echo(c net.Conn, shout string, delay time.Duration) {fmt.Fprintln(c, "\t", strings.ToUpper(shout))time.Sleep(delay)fmt.Fprintln(c, "\t", shout)time.Sleep(delay)fmt.Fprintln(c, "\t", strings.ToLower(shout))
}func handleConn(c net.Conn) {input := bufio.NewScanner(c)for input.Scan() {echo(c, input.Text(), 1*time.Second)}// 注意: 忽略input.Err()中可能的错误c.Close()
}
echo
函数:定义echo
函数,接收net.Conn
、字符串和延迟时间参数,先将字符串转为大写输出,延迟后输出原字符串,再延迟后转为小写输出,模拟真实回声效果。handleConn
函数:在handleConn
函数中,使用bufio.NewScanner
读取客户端输入,循环调用echo
函数处理输入内容,同样忽略了输入错误处理,处理完后关闭连接。
func main() {conn, err := net.Dial("tcp", "localhost:8000")if err!= nil {log.Fatal(err)}defer conn.Close()go mustCopy(os.Stdout, conn)mustCopy(conn, os.Stdin)
}
netcat
程序:客户端程序使用net.Dial
连接服务器,通过两个mustCopy
调用,一个将标准输入发送到服务器,另一个将服务器回复输出到标准输出,主 goroutine 处理输入,另一个 goroutine 处理输出,输入结束时程序停止。
实现并发回声效果
func handleConn(c net.Conn) {input := bufio.NewScanner(c)for input.Scan() {go echo(c, input.Text(), 1*time.Second)}// 注意: 忽略input.Err()中可能的错误c.Close()
}
为使回声真正并发,在调用echo
函数时添加go
关键字,让每个回声处理在单独的 goroutine 中执行,实现多个回声在时间上相互重合的并发效果。在添加go
关键字实现并发时,要考虑net.Conn
并发调用的安全性。
通道
通道是 Go 程序中 goroutine 之间的通信机制,可看作是特定类型元素的导管,如chan int
表示存放int
类型元素的通道。它是 goroutine 间发送特定值的通信桥梁,是并发编程中实现同步和数据传递的重要工具。
ch := make(chan int)
- 创建方式:使用内置的
make
函数创建通道,如ch := make(chan int)
。通道和map
类似,是引用类型,复制或传递时传递的是引用,指向同一份数据结构,其零值为nil
。同种类型通道可用==
比较,若为同一通道引用则比较值为true
,也可与nil
比较 。
ch <- x // 发送语句
x = <-ch // 接收语句
<-ch // 丢弃
- 发送与接收:通道有发送(
send
)和接收(receive
)两个主要操作,使用<-
操作符。发送语句如ch <- x
;接收语句如x = <-ch
,也可丢弃接收结果,如<-ch
。
close(ch)
- 关闭操作:可使用
close
函数关闭通道,关闭后发送操作会导致宕机,接收操作会获取已发送的值,通道为空时接收立即完成并获取元素类型零值 。
类型
ch = make(chan int) // 无缓冲通道
ch = make(chan int, 0) // 无缓冲通道
ch = make(chan inr, 3) // 容量为3的缓冲通道
使用简单make
调用创建的是无缓冲通道,make
还可接受第二个可选参数表示通道容量。容量为 0 时是无缓冲通道,如ch = make(chan int)
或ch = make(chan int, 0)
;指定容量(如ch = make(chan int, 3)
)则为缓冲通道 ,下面会再进行介绍缓冲通道 。
无缓冲通道
无缓冲通道上的发送操作会阻塞,直到有另一个 goroutine 在对应通道上执行接收操作,此时值传送完成,两个 goroutine 可继续执行;反之,接收操作先执行也会阻塞,直到有 goroutine 发送值。这种通信机制使发送和接收的 goroutine 同步化,所以无缓冲通道也叫同步通道 。
func main() {conn, err := net.Dial("tcp", "localhost:8000")if err!= nil {log.Fatal(err)}done := make(chan struct{})go func() {io.Copy(os.Stdout, conn) // 注意: 忽略错误log.Println("done")done <- struct{}{} // 指示主 goroutine}()mustCopy(conn, os.Stdin)conn.Close()<-done // 等待后台 goroutine 完成
}
示例:客户端程序基础上,通过创建无缓冲通道done
来同步主 goroutine 和后台 goroutine。主 goroutine 等待从done
通道接收值,后台 goroutine 在完成任务(如io.Copy
操作)后,向done
通道发送一个值,主 goroutine 接收到值后才继续执行后续操作(如关闭连接 )。当用户关闭标准输入流,mustCopy
返回,后台 goroutine 记录消息并向done
通道发送值,主 goroutine 接收到值后关闭连接,保证程序按预期顺序执行 。
消息与事件的概念
- 通过通道发送消息时,不仅消息的值重要,通信本身及发生时间也很关键,这种用于同步、不携带额外信息的消息称为事件。可使用
struct{}
元素类型的通道来强调同步功能,bool
或int
类型通道也可以 。
管道
管道是利用通道连接 goroutine 的一种方式,使一个 goroutine 的输出成为另一个的输入。通过通道在多个包含无限循环的 goroutine 间进行全生命周期通信 。
func main() {naturals := make(chan int)squares := make(chan int)// countergo func() {for x := 0; x < 100; x++ {naturals <- x}close(naturals)}()// squarergo func() {for x := range naturals {squares <- x * x}close(squares)}()// printer (在主 goroutine 中)for x := range squares {fmt.Println(x)}
}
-
示例:程序由
counter
、squarer
、printer
三个 goroutine 和两个通道组成。counter
产生自然数序列并通过naturals
通道发送给squarer
,squarer
计算平方后通过squares
通道发送给printer
,printer
输出结果 。 -
通道关闭处理:当发送方知道无更多数据发送时,可关闭通道告知接收方停止等待 。如
counter
在发送一定数量元素(如 100 个 )后关闭naturals
通道,squarer
接收到通道关闭信号后处理并关闭squares
通道 。接收操作有变体,可返回接收值和表示是否成功的布尔值 ,利用此特性可判断通道是否关闭并处理 。也可使用range
循环语法,在通道接收完所有值后自动结束循环 ,简化通道关闭和数据处理逻辑 。 -
关闭通道的必要性:不是必须的,仅在需通知接收方数据发送完毕时进行 。通道回收由垃圾回收器根据可访问性决定,与文件关闭操作不同 。关闭已关闭通道会导致宕机,关闭通道还可作为广播机制 。
缓冲通道
ch = make(chan string, 3)
- 创建:缓冲通道通过
make
函数创建,可设置容量参数,如ch = make(chan string, 3)
创建一个能容纳三个字符串的缓冲通道 。 - 发送与接收操作:发送操作向通道队列尾部插入元素,接收操作从头部移除元素 。通道满时发送操作阻塞,通道空时接收操作阻塞,部分填满时发送和接收操作不阻塞 。可通过
cap
函数获取通道容量,len
函数获取当前元素个数 。
func mirroredQuery() string {responses := make(chan string, 3)go func() { responses <- request("asia.gopl.io") }()go func() { responses <- request("europe.gopl.io") }()go func() { responses <- request("americas.gopl.io") }()return <-responses // return the quickest response
}
示例:它并发向三个镜像地址发送请求,将响应通过缓冲通道发送,只接收最早返回的响应 。缓冲通道在并发场景中解耦发送和接收 goroutine 的作用 。同时使用无缓冲通道可能导致 goroutine 泄漏(长时间阻塞无法结束 ),要合理选择无缓冲或缓冲通道以及设置缓冲通道容量。
缓冲通道与性能
以蛋糕店厨师工作场景类比,无缓冲通道类似厨师需等待下一个接收,同步性强;缓冲通道可容纳一定量任务,容量为 1 时可消除速率差异,容量更大可应对更大速率波动 。还提到可通过创建额外 goroutine 辅助处理任务,以优化程序性能 。
并行循环
- 初步并行:直接在循环中添加
go
关键字启动 goroutine 进行并行处理,但此版本存在问题,外层 goroutine 可能在内部 goroutine 完成任务前就返回,导致任务未真正完成 。 - 使用通道同步:创建无缓冲通道,内层 goroutine 完成任务时向通道发送信号,外层 goroutine 通过接收通道信号计数,等待所有任务完成 。此过程需注意循环变量在闭包中的使用问题,避免 goroutine 读取到错误的变量值 。
- 处理错误返回:为使外层 goroutine 能获取内层 goroutine 执行函数的错误,创建用于接收错误的通道,内层 goroutine 将错误发送到通道,外层 goroutine 接收并处理错误 。但简单处理方式可能导致 goroutine 泄漏(如遇到错误时未正确结束 goroutine ),可通过使用有足够容量的缓冲通道或其他方案解决 。
- 返回处理结果:进一步改进,创建缓冲通道,内层 goroutine 将生成的内容及错误信息发送到通道,外层 goroutine 接收并处理 。
使用sync.WaitGroup
同步
func makeThumbnails6(filenames <-chan string) int64 {sizes := make(chan int64)var wg sync.WaitGroup // 工作goroutine的个数for f := range filenames {wg.Add(1)go func(f string) {defer wg.Done()thumb, err := thumbnail.ImageFile(f)if err != nil {log.Println(err)return}info, _ := os.Stat(thumb) // 可以忽略错误sizes <- info.Size()}(f)}// closergo func() {wg.Wait()close(sizes)}()var total int64for size := range sizes {total += size}return total
}
为更好地控制和等待所有 goroutine 结束,引入sync.WaitGroup
。在启动每个工作 goroutine 前使用Add
方法增加计数,工作 goroutine 结束时调用Done
方法减少计数,主 goroutine 通过Wait
方法阻塞等待计数为 0,即所有工作 goroutine 完成 。同时,结合通道传递处理结果(如文件大小 ),实现更健壮的并行处理 。
使用 select 多路复用
select {
case <-ch1://...
case x := <-ch2://...use x...
case ch3 <- y://...
default://...
}
select
语句用于在多个通道操作中进行选择,实现多路复用。它类似switch
语句,有一系列情况和可选的默认分支,每个情况指定一次通道上的发送或接收操作及关联代码块 。select
会一直等待,直到有一个通道操作可执行,然后执行对应语句,其他未满足条件的操作不会执行;若没有对应情况且无默认分支,select
将永远等待 。
示例
func main() {fmt.Println("Commencing countdown.")tick := time.Tick(1 * time.Second)for countdown := 10; countdown > 0; countdown-- {fmt.Println(countdown)<-tick}launch()
}
- 火箭发射倒计时:以火箭发射倒计时为例,最初的倒计时程序通过
time.Tick
函数按固定时间间隔发送事件进行倒计时 。为实现可取消发射,启动一个 goroutine 从标准输入读取字符,若成功则向abort
通道发送值 。使用select
语句等待time.Tick
通道的计时事件或abort
通道的取消事件 。还可结合time.After
函数设置超时,若在指定时间(如 10s )内未收到取消事件则开始发射 。
ch := make(chan int, 1)
for i := 0; i < 10; i++ {select {case x := <-ch:fmt.Println(x) // "0" "2" "4" "6" "8"case ch <- i:}
}
- 通道状态操作选择:对于缓冲区大小为 1 的通道
ch
,通过select
语句根据通道状态(空或满 )及循环变量i
的奇偶性,决定是从通道接收还是向通道发送数据 。当多个情况同时满足时,select
随机选择一个执行 。
func main() {//...创建中止通道...fmt.Println("Commencing countdown. Press return to abort.")tick := time.Tick(1 * time.Second)for countdown := 10; countdown > 0; countdown-- {fmt.Println(countdown)select {case <-tick:// 什么操作也不执行case <-abort:fmt.Println("Launch aborted!")return}}launch()
}
- 带输出的倒计时:改进火箭发射倒计时程序,在每次迭代中使用
select
语句使程序等待 1s 以检测中止事件,同时输出倒计时数值 。
注意事项
ticker := time.NewTicker(1 * time.Second)
<-ticker.C // 从ticker的通道接收
ticker.Stop() // 造成ticker的goroutine终止select {
case <-abort:fmt.Printf("Launch aborted!\n")return
default:// 不执行任何操作
}
time.Tick
函数使用可能导致的 goroutine 泄漏问题,因为即使不再接收其通道事件,对应的 goroutine 仍在运行 。建议使用time.NewTicker
创建定时器,使用完后通过Stop
方法终止相关 goroutine 。select
语句可实现非阻塞通信,通过添加默认分支,在没有通道操作就绪时立即执行默认代码块 ,重复此操作可实现对通道轮询 。nil
通道上的操作永远不会被选中 ,可利用这一特性开启或禁用特定情况 。
取消
在一些场景下,需要让 goroutine 停止当前任务,如 Web 服务器处理客户端请求时客户端断开连接 。但直接终止一个 goroutine 会使共享变量状态不确定,且难以准确知道有多少 goroutine 在工作,简单通过通道发送固定数量事件来取消多个 goroutine 存在计数不准确、操作卡住等问题 。
基于通道关闭的广播式取消机制
var done = make(chan struct{})
func cancelled() bool {select {case <-done:return truedefault:return false}
}
利用通道关闭特性实现广播式取消 。创建取消通道done
,不向其发送值,而是通过关闭它来表明取消操作 。定义cancelled
函数,使用select
语句检测done
通道是否关闭 ,若关闭则返回true
,否则返回false
。同时,启动一个 goroutine 读取标准输入,一旦检测到输入(如用户按回车键 ),就关闭done
通道广播取消事件 。
// 当检测到输入时取消遍历
go func() {os.Stdin.Read(make([]byte, 1)) // 读一个字节close(done)
}()for {select {case <-done:// 耗尽fileSizes以允许已有的goroutine结束for range fileSizes {// 不执行任何操作}returncase size, ok := <-fileSizes://...}
}
- 主 goroutine:在主 goroutine 的
select
语句中添加从done
通道接收的情况 。当接收到取消信号时,先耗尽fileSizes
通道中的值(防止卡住 ),然后返回 。
func walkDir(dir string, n *sync.WaitGroup, fileSizes chan<- int64) {defer n.Done()if cancelled() {return}for _, entry := range dirents(dir) {//...}
}
walkDir
函数:walkDir
函数开始时轮询取消状态,若检测到取消(cancelled
为true
),则立即返回 ,不再执行后续操作 。在walkDir
循环中也可进行取消状态轮询 ,避免取消后创建新的 goroutine 。
func dirents(dir string) []os.FileInfo {select {case sema <- struct{}{}: // 获取令牌case <-done:return nil // 取消}defer func() { <-sema }() // 释放令牌//...read directory...
}
dirents
函数:在dirents
函数中,使用select
语句同时处理获取信号量令牌和检测取消通道done
,若检测到取消则直接返回nil
,实现更快速的取消响应 。通过这些措施,当取消事件发生时,后台 goroutine 能迅速停止,main
函数返回,程序退出。
alkDir函数**:
walkDir函数开始时轮询取消状态,若检测到取消(
cancelled为
true ),则立即返回 ,不再执行后续操作 。在
walkDir`循环中也可进行取消状态轮询 ,避免取消后创建新的 goroutine 。
func dirents(dir string) []os.FileInfo {select {case sema <- struct{}{}: // 获取令牌case <-done:return nil // 取消}defer func() { <-sema }() // 释放令牌//...read directory...
}
dirents
函数:在dirents
函数中,使用select
语句同时处理获取信号量令牌和检测取消通道done
,若检测到取消则直接返回nil
,实现更快速的取消响应 。通过这些措施,当取消事件发生时,后台 goroutine 能迅速停止,main
函数返回,程序退出。
参考资料:《Go程序设计语言》
相关文章:
Go:goroutine 和通道
goroutine f() // 等待 f() 返回 go f() // 新建一个调用 f() 的 goroutine,不用等待在 Go 语言里,goroutine 是并发执行的活动单元。与顺序执行程序不同,在有多个 goroutine 的并发程序中,不同函数可同时执行。程序启动时&…...
盛水最多的容器问题详解:双指针法与暴力法的对比与实现
文章目录 问题描述方法探讨方法一:暴力法(Brute Force)思路代码实现复杂度分析 方法二:双指针法(Two Pointers)思路正确性证明代码实现复杂度分析 方法对比总结 摘要 盛水最多的容器(Container …...
VMWare 16 PRO 安装 Rocky8 并部署 MySQL8
VMWare 16 PRO 安装 Rocky8 并部署 MySQL8 一.Rocky OS 下载1.官网 二.配置 Rocky1.创建新的虚拟机2.稍后安装系统3.选择系统模板4.设置名字和位置5.设置大小6.自定义硬件设置核心、运存和系统镜像7.完成 三.启动安装1.上下键直接选择安装2.回车安装3.设置分区(默认…...
日常学习开发记录-slider组件
日常学习开发记录-slider组件 从零开始实现一个优雅的Slider滑块组件前言一、基础实现1. 组件结构设计2. 基础样式实现3. 基础交互实现 二、功能增强1. 添加拖动功能2. 支持范围选择3. 添加垂直模式 三、高级特性1. 键盘操作支持2. 禁用状态 五、使用示例六、总结 从零开始实现…...
AIDL 中如何传递 Parcelable 对象
目录 1. 直接在 AIDL 中定义 Parcelable 对象2. 自定义 Parcelable 对象的传递3. 以 Rect 类为例的 Parcelable 实现4. 注意安全性5. 小结1. 直接在 AIDL 中定义 Parcelable 对象 背景说明 从 Android 10(API 级别 29)开始,AIDL 允许直接在 .aidl 文件中定义 Parcelable 对…...
LVGL实战训练——计算器实现
目录 一、简介 二、部件知识 2.1 按钮矩阵部件(lv_btnmatrix) 2.1.1 按钮矩阵部件的组成 2.1.2 按钮文本设置 2.1.3 按钮索引 2.1.4 按钮宽度 2.1.5 按钮属性 2.1.6 按钮互斥 2.1.7 按钮文本重着色 2.1.8 按钮矩阵部件的事件 2.1.9 按钮矩阵部件的 API 函数 2.2…...
代码随想录算法训练营Day30
力扣452.用最少数量的箭引爆气球【medium】 力扣435.无重叠区间【medium】 力扣763.划分字母区间【medium】 力扣56.合并区间【medium】 一、力扣452.用最少数量的箭引爆气球【medium】 题目链接:力扣452.用最少数量的箭引爆气球 视频链接:代码随想录 题…...
AIDL 语言简介
目录 软件包类型注释导入AIDL 的后端AIDL 语言大致上基于 Java 语言。AIDL 文件不仅定义了接口本身,还会定义这个接口中用到的数据类型和常量。 软件包 每个 AIDL 文件都以一个可选软件包开头,该软件包与各个后端中的软件包名称相对应。软件包声明如下所示: package my.pac…...
经典算法 判断一个图中是否有环
判断一个图中是否有环 问题描述 给一个以0 0结尾的整数对列表,除0 0外的每两个整数表示一条连接了这两个节点的边。假设节点编号不超过100000大于0。你只要判断由这些节点和边构成的图中是否存在环。存在输出YES,不存在输出NO。 输入样例1 6 8 5 3 …...
Transformer-PyTorch实战项目——文本分类
Transformer-PyTorch实战项目——文本分类 ———————————————————————————————————————————— 【前言】 这篇文章将带领大家使用Hugging Face里的模型进行微调,并运用在我们自己的新项目——文本分类中。需要大家提前下…...
Linux-服务器负载评估方法
在 Linux 服务器中,top 命令显示的 load average(平均负载)反映了系统在特定时间段内的负载情况。它通常显示为三个数值,分别代表过去 1 分钟、5 分钟和 15 分钟的平均负载。 1. 什么是 Load Average? Load average …...
Transformer编程题目,结合RTX 3060显卡性能和市场主流技术
以下是10道针对4年经验开发者的Transformer编程题目,结合RTX 3060显卡性能和市场主流技术,每题包含模型选择和实现逻辑描述: 题目1:医疗报告结构化提取 模型选择:BioBERT-base 要求: 开发从PDF医疗报告中提…...
Web三漏洞学习(其二:sql注入)
靶场:NSSCTF 、云曦历年考核题 二、sql注入 NSSCTF 【SWPUCTF 2021 新生赛】easy_sql 这题虽然之前做过,但为了学习sql,整理一下就再写一次 打开以后是杰哥的界面 注意到html网页标题的名称是 “参数是wllm” 那就传参数值试一试 首先判…...
VLAN的知识
1.什么是VLAN? VLAN是虚拟局域网,逻辑隔离广播域和网络区域 是一种通过将局域网内的设备逻辑地划分为一个个网络的技术 2.对比逻辑网络分割和物理网络分割? 逻辑网络分割是VLAN,隔离广播域和网络区域 物理网络分割是路由&…...
RFID 赋能部队智能物联网仓储建设:打造信息化高效解决方案
在当今军事现代化进程的宏大背景下,部队后勤保障工作无疑占据着举足轻重的地位,而仓储管理作为其中的核心环节,更是至关重要。传统的仓储管理模式在面对当下物资种类繁杂、数量庞大的现状时,已显得力不从心,效率低下、…...
结构型屏蔽在高频电子设备中的应用与优化
在当今高度电子化的时代,随着电子产品工作频率不断提高,设备内部温度上升,电磁环境日趋复杂,电磁兼容(EMC)问题成为设计和制造过程中必须重点解决的问题。EMC不仅关系到设备自身的稳定运行,更涉…...
【教程】Ubuntu修改ulimit -l为unlimited
转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~ 目录 问题描述 解决方法一 解决方法二 解决方法三 (终极) 问题描述 查系统资源限制 ulimit -l如果返回的是 64 或其他较小值,那么RDM…...
【HDFS】BlockPlacementPolicyRackFaultTolerant#getMaxNode方法的功能及具体实例
方法参数说明: numOfChosen:已经选择的节点数numOfReplicas:还需要选择的副本数方法的返回值是一个长度为2的数组:[调整后的要选出多少个节点(不包括已经选择的), 每个机架最大能选择的节点数] @Overrideprotected int[] getMaxNodesPerRack(int numOfChosen, int numOfR…...
水污染治理(生物膜+机器学习)
文章目录 **1. 水质监测与污染预测****2. 植物-微生物群落优化****3. 系统设计与运行调控****4. 维护与风险预警****5. 社区参与与政策模拟****挑战与解决思路****未来趋势** 前言: 将机器学习(ML)等人工智能技术融入植树生物膜系统ÿ…...
数模小白变大神的日记2025.4.15日
分工 1.论文:mathtype (Latex) 2.建模;相应的建模知识与撰写方法,写摘要 3.编程:matlab、SPSs、(Python) 评价模型 1. 层次分析法 ①层次分析法是一种多目标、多准则的决策问题 ②层次分析法是一种主观加权法 ③层次分析法通过以下步骤实现: 1.构…...
STM32提高篇: 以太网通讯
STM32提高篇: 以太网通讯 一.以太网通讯介绍二.W5500芯片介绍1.W5500芯片特点2.W5500应用目标3.接入框图 三.驱动移植四.tcp通讯五.udp通讯六.http_server 一.以太网通讯介绍 以太网(Ethernet)是一种计算机局域网技术。IEEE组织的IEEE 802.3标准制定了以…...
4-15记录(冒泡排序,快速选择排序)
算法稳定 简单选择排序的实质就是最后一个和第一个比较,小,就换位置,然后继续用最后一个数字和第二个比较,以此类推。 但是算法不稳定,本来下划线的2在后面,但是经过算法后去了前面 快速排序 实现过程&am…...
Ubuntu系统18.04更新驱动解决方法
原始是:ubuntu18.04里面的驱动是470,对应cuda11.4 现在需要更新为525,对应cuda为12.0 实现: 1、打开终端 Ctrl Alt T2、使用 lspci 命令(快速查看显卡型号) lspci | grep -i vga3、终端输入 ubuntu-d…...
Rocky Linux 9.x 基于 kubeadm部署k8s
搭建集群使用docker下载K8s,使用一主两从模式 主机名IP地址k8s- master192.168.1.141k8s- node-1192.168.1.142k8s- node-2192.168.1.143 一:准备工作 VMware Workstation Pro新建三台虚拟机Rocky Linux 9(系统推荐最小化安装) …...
MATLAB程序实现了一个物流配送优化系统,主要功能是通过遗传算法结合四种不同的配送策略,优化快递订单的配送方案
%% 主函数部分 % function main()clear; clc; close all;% 生成或加载算例 filename = D:\快递优化\LogisticsInstance.mat; if ~exist(filename, file)instance = generate_instance();save(filename, -struct, instance); elseinstance = load(filename); end% 遗传算法参数配…...
利用宝塔面板搭建RustDesk服务
一、介绍 1.1官网 https://rustdesk.com/ 1.2github仓库 https://github.com/rustdesk/rustdesk 1.3特点 RustDesk 支持多种操作系统,包括 Windows、macOS、Linux、Android 和 iOS。它甚至提供网页版客户端,可以在浏览器中直接使用。 用户可以通过…...
前端与Java后端交互出现跨域问题的14种解决方案
跨域问题是前端与后端分离开发中的常见挑战,以下是14种完整的解决方案: 1 前端解决方案( 开发环境代理) 1.1 Webpack开发服务器代理 // vue.config.js 或 webpack.config.js module.exports {devServer: {proxy: {/api: {target: http://localhost:8…...
PBKDF2全面指南(SpringBoot实现版)
文章目录 第一部分:PBKDF2基础概念1. 什么是PBKDF2?2. 为什么需要PBKDF2?3. PBKDF2的工作原理4. PBKDF2与其他密码散列函数的比较第二部分:在Java和SpringBoot中使用PBKDF21. Java内置的PBKDF2支持2. SpringBoot中集成PBKDF22.1 添加依赖2.2 配置PBKDF2密码编码器2.3 自定义…...
基于RV1126开发板的rknn-toolkit-lite使用方法
1. rknn-toolkit-lite介绍 rknn-toolkit-lite是用于python算法的推理的组件,当前已经在EASY-EAI-Nano完成适配,用户可以用它进行深度学习算法的纯python开发。而且同时支持已经进行了预编译的模型,短短几行代码即可完成算法的推理,…...
一款轻量级的PHP地址发布页面源码
源码介绍 一款轻量级的PHP链接发布页面源码,适合快速搭建个性化的链接导航网站,支持动态链接管理和多种风格模板切换 1:后台登录地址为/admin/login.php,提供便捷的配置入口。 2:默认用户名是admin,密码为…...
分布式计算领域的前沿工具:Ray、Kubeflow与Spark的对比与协同
在当今机器学习和大数据领域,分布式计算已成为解决大规模计算问题的关键技术。本文将深入探讨三种主流分布式计算框架——Ray、Kubeflow和Spark,分析它们各自的特点、应用场景以及如何结合它们的优势创建更强大的计算平台。 Spark批量清洗快,…...
【专题刷题】双指针(一)
📝前言说明: 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录,按专题划分每题主要记录:1,本人解法 本人屎山代码;2,优质解法 优质代码;3,精益求精,…...
火山引擎旗下防御有哪些
首先,我需要确认用户是不是打错了,比如把“引擎”当成了“云”,或者他们真的想了解火山引擎的防御机制。火山引擎是字节跳动旗下的云服务平台,类似于阿里云或腾讯云,所以用户可能想了解的是其安全防护措施。 接下来&am…...
python程序打包——nuitka使用
目前python打包成exe的工具主要有:PyInstaller Briefcase py2exe py2app Nuitka CX_Freeze等。 不同于C代码,可以直接编译成可执行的exe文件,或者js代码在浏览器中就能执行,python代码必须通过python解释器来运行,…...
编写了一个专门供强化学习玩的贪吃蛇小游戏,可以作为后续学习的playgraound
文章目录 **试玩效果****项目背景****核心设计思路****代码亮点解析****与强化学习算法的对接示例****扩展方向****总结****完整代码**把训练一个会玩小游戏的智能体,作为学习强化学习的一个目标,真的是很有乐趣的一件事。我已经不知为此花费了多少日夜了。如今已是着魔了一般…...
chain_type=“stuff 是什么 ? 其他方式有什么?
chain_type="stuff 是什么 ? 其他方式有什么? 目录 chain_type="stuff 是什么 ? 其他方式有什么?1. `chain_type="stuff"`2. `chain_type="map_reduce"`3. `chain_type="refine"`4. `chain_type="map_rerank"`在 LangCh…...
在IDEA里面建立maven项目(便于java web使用)
具体步骤: 第一次有的电脑你再创建项目的时候右下角会提醒你弹窗:让你下载没有的东西 一定要下载!!可能会很慢 运行结果: 因为他是默认的8080端口所以在运行的时候输入的url如下图: 新建了一个controller代…...
MyBatis 详解
1. 什么是 MyBatis? MyBatis 是一款优秀的 持久层框架,它通过 XML 或注解配置,将 Java 对象(POJO)与数据库操作(SQL)进行灵活映射,简化了 JDBC 的复杂操作。 核心思想:S…...
郑州工程技术学院党委书记甘勇一行莅临埃文科技调研交流
为深化产教融合、推动人工智能领域人才培养与产业需求精准对接,2025年4月9日下午,郑州工程技术学院党委书记甘勇、河南省人工智能产业创新发展联盟执行秘书长孟松涛等一行莅临埃文科技调研交流。 一、聚焦技术前沿 共话AI产业变革 座谈会上,…...
AI应用开发之扣子第一课-夸夸机器人
首先,进入官网:点击跳转至扣子。 1.创建智能体 登录进网站后,点击左上角+图标,创建智能体,输入智能体名称、功能介绍 2.输入智能体提示词 在“人设与回复逻辑”输入以下内容: # 角色 你是一…...
Node.js 数据库 CRUD 项目示例
希望使用Nodejs操作数据库做CRUD,用deepseek实战搜索“使用Nodejs对数据库表做CRUD的项目例子”,找到了解决方案,如下图所示: 项目结构 nodejs-crud-example/ ├── config/ │ └── db.js # 数据库连接配置 ├──…...
ESP8266/32作为AVR编程器(ISP programmer)的使用介绍
ESP8266作为AVR编程器( ISP programmer)的使用介绍 🌿ESP8266自带库例程:https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266AVRISP📍支持ESP8266/32的ESP_AVRISP其它开源工程(个人没有再去验证)&…...
union all几个常见问题及其解决方案
UNION ALL 是 SQL 中用于合并两个或多个 SELECT 语句结果集的操作符。与 UNION 不同,UNION ALL 不会去除重复的记录,它简单地将一个查询的结果附加到另一个查询的结果之后。尽管 UNION ALL 相对来说更高效(因为它不需要检查重复项)…...
21.C++11
1.列表初始化 1.1C11中的{} •C11以后想统⼀初始化⽅式,试图实现⼀切对象皆可⽤{}初始化,{}初始化也叫做列表初始化。 • 内置类型⽀持,⾃定义类型也⽀持,⾃定义类型本质是类型转换,中间会产⽣临时对象,最…...
【交叉编译】目标机编译安装对应依赖库总结
1、解压目标机交叉编译工具链 # 创建工具链存放目录(可选) sudo mkdir -p /opt/toolchain# 解压到目标路径(示例路径:/opt/toolchain) sudo tar -xzvf 目标主机编译工具链.tar.gz -C /opt/toolchain# 查看解压后的目录…...
Docker华为云创建私人镜像仓库
Docker华为云创建私人镜像仓库 在华为云官网的 产品 中搜索 容器镜像服务 : 或者在其他页面的搜索栏中搜索 容器镜像服务 : 进入到页面后,点击 创建组织 (华为云的镜像仓库称为组织): 设置组织名字后&…...
【15】数据结构之基于树的查找算法篇章
目录标题 二叉排序树 Binary Sort Tree二叉排序树的插入二叉树排序树的删除二叉排序树的查找二叉排序树的调试与代码集合 平衡二叉树-AV树平衡二叉树的平衡化旋转平衡二叉树的代码调试与代码集合 B树B树的查找B树的插入B树和B*树 二叉排序树 Binary Sort Tree 二叉…...
自定义类型之结构体
1.结构体类型概述 结构体类型是一种用户自定义的数据类型,用于将不同类型的数据组合成一个整体。在C语言中,结构体使用struct关键字定义,由一系列具有相同类型或不同类型的数据构成的数据集合,也称为结构。结构体中的数据在逻辑上…...
SGFormer:卫星-地面融合 3D 语义场景补全
论文介绍 题目:SGFormer: Satellite-Ground Fusion for 3D Semantic Scene Completion 会议:IEEE / CVF Computer Vision and Pattern Recognition Conference 论文:https://www.arxiv.org/abs/2503.16825 代码:https://githu…...
应急响应篇钓鱼攻击邮件与文件EML还原蠕虫分析线索定性处置封锁
钓鱼邮件的eml中会有 邮件服务器地址域名(发信人)发送的本地IP和主机名发送的内容以及附件 邮件钓鱼: 攻击者目的:通过发信人,附件,取得突破 定性钓鱼邮件 威胁情报,人工分析来源,…...