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

Go红队开发—并发编程

文章目录

  • 并发编程
    • go协程
      • chan通道
      • 无缓冲通道
      • 有缓冲通道
      • 创建⽆缓冲和缓冲通道
    • 等协程
      • sync.WaitGroup同步
      • Runtime包
        • Gosched()
        • Goexit()
      • 区别
    • 同步变量
      • sync.Mutex互斥锁
      • atomic原子变量
    • Select
    • Ticker定时器
    • 控制并发数量
      • 核心机制
  • 并发编程阶段练习
    • 重要的细节
    • 端口扫描
    • 股票监控

并发编程

go协程

chan通道

必要条件:
从通道取元素的时候要先关闭通道,程序才知道你不往通道输入了,才能取出元素来进行操作,否则会由于通道没有被关闭,range 操作会一直等待新的数据到来,导致程序陷入死锁状态。

close关闭通道不是必要的,但是如果你不关闭,还一直读取里面的东西的话, 你要保证你有源源不断的内容进入到通道中不能让他处于死锁状态。

//在go中的通道
var 变量名 = make(chan 类型)
var c = make(chan int)
//通道可以给很多数据,取出来的时候可以一个个的pop一样,每次取一个就pop一个
c <- 123
c <- 456
close(c) //你要取出内容的时候要先关闭通道,程序才知道你不要往通道输入了,要打印了。否则打印失败,因为一直处于等待状态会死锁。
for v := range c{fmt.Println(v)  //这里会依次打印123,456
}

个人理解:

0.go协程感觉可以理解为比线程更加细的,同时也更快的一种东西,它能够实现多线程操作
(其实应该说多协程操作,就是能同时干很多事情)1.go的协程main运行结束前他能够做抢占运行权的能力,但是一旦main运行结束他也就寄寄了。2.`通道(Channels)`:这个很重要,在协程中如果你没有用缓冲通道,就代表你使用go协程的话,能够进到协程里面运行的概率比较小,因为main一直抢占执行权,如果很快就执行完main的话就没go协程啥事情了。3.缓冲区:当你在使用Channels的时候且使用⽆缓冲通道,如果造成了阻塞->那么就是你要取通道的东西,但是需要等通道有东西取的时候才能往下走程序。
可如果你make有缓冲通道的时候,就不会造成阻塞的状态,前提是拿缓冲通道东西的时候一定要有东西,否则他会一直等待,就像上面的for循环,他就是一直等待导致了死锁,所以报错,不是因为他代码错误哈!
那么同时,如果你缓冲区有东西,就算进入了go协程也会很有可能被main程序抢占,`但如果缓冲区为空,那么main在取出数据的时候会因没有数据而等待某个协程给数据到通道然后取出来。`

对通道与协程之间的关系详细解释:(核心是能够让goroutine协程能够互相通信)

Go 提供了⼀种称为通道的机制,⽤于在 goroutine 之间共享数据。
Channels 通道可以共享内置、命名、结构和引⽤类型的值和 指针。
两种类型的通道:⽆缓冲通道和缓冲通道。
⽆缓冲通道⽤于执⾏goroutine之间的同步通信,⽽缓冲通道⽤于执⾏异步通信。
⽆缓冲通道保证在发送和接收发⽣的瞬间执⾏两个 goroutine 之间的交换。缓冲通道没有这样的保证。 协程之间⽆法直接通信,但可以使⽤通道来使协程之间互相通信
通道分为有缓冲和⽆缓冲,有缓冲,容量设置后,可以异步读取数据,不会堵塞,⽆缓冲的话, 放⼀个数据就必须取出来,否则就会阻塞

无缓冲通道

无缓冲通道的特性
发送:发送操作会阻塞,直到有 Goroutine 接收该值。
接收:接收操作会阻塞,直到有 Goroutine 发送一个值。举例子:
`假设你要在main中进行发送数据且读取数据是行不通的`,一定要开一个go协程发送数据,这样的话你的main就不会阻塞,因为go和main是互不干扰的,但是go协程可能执行权限轮不到他,main会抢占,所以这时候就能解释为什么要用sleep来休眠,等待go协程去执行发送通道了。
(当然sleep这种方式只是演示,实际中不会用,有其他方法解决)

有缓冲通道

在带缓冲的通道中,只要缓冲区未满,发送不会阻塞
`只要缓冲区未空,接收不会阻塞`

创建⽆缓冲和缓冲通道

make(chan int) //整型⽆缓冲通道,这里就是没有缓冲make(chan int ,1) //整型有缓冲通道,1也代表有一个缓冲//注意甄别:缓冲和通道是不一样的,通道是通道,你缓冲是拿来缓冲的,懂吧hhh?

示例代码:

package mainimport ("fmt""math/rand""time"
)var ch = make(chan int, 10)func Ion() {rand.Seed(time.Now().UnixNano())intn := rand.Intn(100)fmt.Println("随机数为:", intn)ch <- intnch <- 2  //对比数据ch <- 55  //对比数据
}
func main() {defer close(ch) //main关闭后,关闭通道//ch <- 2如果这里注释打开,就不会进入下面的go协程,因为主程序很快抢占回来执行结束,那么go的协程在go 的main运行完就无法运行了go Ion()fmt.Println("Wait...")data := <-chfmt.Println("值为:", data)  //最终取出来的是随机数fmt.Println("Done")
}

等协程

sync.WaitGroup同步

在上述中学到了,通道取出来数据是需要等待通道中有数据的,不然会阻塞,那么这当然是一种很好的同步方式,但是当我们go协程开启后,与main运行不相干的时候,main是不管协程的。main:“抢得过我再说”。

三个核心

var wg sync.WaitGroup
wg.Add(1)
wg.Done()  //这个函数里面其实是执行wg.Add(-1)
wg.Wait()

单单看上面三个函数其实就能理解了

当我们需要go协程与主函数同步执行的时候,可以用add添加,然后wait等待,那么怎么判断是否需要继续wait呢,那么就是用done来判断了,如果一直减到0的时候就不用wait了

示例代码:

//这样的话就不用sleep函数了,sleep我们也不清楚要多少秒才能让协程完成,所以肯定是需要这种同步机制来操作
func wq() {  var wg sync.WaitGroup//比如你每次进入go协程之前都add一下for i := 0; i < 10; i++ {fmt.Println("进入go协程之前add一下")wg.Add(1)go func(n int) {fmt.Println("我是协程:", n)defer wg.Done() //通知一下go协程OK了,然后defer是为了防止中途报错无法释放。defer闭包释放好一点。}(i)}//这里需要等待wait一下wg.Wait()fmt.Println("等待结束")
}

Runtime包

这个也可以让go协程同步

Gosched()

这个函数是用来等待协程的,如果还有协程在执行,main就会让出执行权利,实现同步

func runtime_go_Gosched() {fmt.Println("演示Gosched")go func() {fmt.Println("go协程执行中...")runtime.Goexit()fmt.Println("go协程还能执行吗...")}()runtime.Gosched() //等待go协程执行完fmt.Println("演示Gosched结束")}
Goexit()

exit顾名思义退出,当你go协程执行的时候可以用这个退出,然后回到外层函数继续执行

func runtime_go_Goexit() {fmt.Println("演示Goexit")go func() {fmt.Println("go协程执行中...")for i := 0; i < 10; i++ {if i > 7 {fmt.Println("go协程退出:", i)runtime.Goexit()}}}()runtime.Gosched() //等待go协程执行完fmt.Println("演示Goexit结束")
}

区别

sync.WaitGroup的作用一般是用于等待一组 goroutine 完成任务,done之后就不会阻塞了,即使你goroutine任务done完的代码后面还有代码任务,那么这时候goroutine就看情况抢占调度了,运气不好或者你main也没啥可执行的了就会直接结束,结束了你的go携程里面就算有代码可能抢不过main调度。runtime就是用于等待或结束一个goruntine的,可以用多个,每一个go携程中都能够

同步变量

sync.Mutex互斥锁

不管是在以前学的线程还是go中的协程,开了多线程后都会面临着几个程序抢占同一个变量的事情,可能会产生死锁问题。

两个函数,加锁和解锁

var Lock sync.Mutex
Lock.Lock()
Lock.Unlock()

简单的示例代码,使用加减法队同一个变量操作,你就会发现不会发生死锁问题
这里采用了x=10,然后循环加减法50次,你会发现基本不会发生一个加分或者减法一直执行的事情发生。

func Mutex_lock() {var (x    int = 10wg   sync.WaitGroupLock sync.Mutex)var Add = func() {defer wg.Done()Lock.Lock()x += 1fmt.Println("x++:", x)Lock.Unlock()}var Sub = func() {defer wg.Done()Lock.Lock()x -= 1fmt.Println("x--:", x)Lock.Unlock()}for i := 0; i < 50; i++ {  //循环50次wg.Add(1)go Add()wg.Add(1)go Sub()}wg.Wait()fmt.Println("结束", x)
}

atomic原子变量

除了Mutex互斥锁能够实现变量的同步操作外,go中还提供了原子变量

因为原子操作不能被中断,所以它需要足够简单,并且要求快速。因此,操作系统层面只对二进制位或整数的原子操作提供了支持。
`原子操作只支持有限的数据类型`,所以多数场景下,往往互斥锁更合适。

这里用同样的一个操作,加减法来实现一个变量的同步。

简单了解一下几个函数(以int类型为例子,其他类型一样的)

//CAS 是 Compare-And-Swap(⽐较并交换)的缩写
//作用是:⽐较内存中的某个值是否等于预期值,如果相等则将其更新为新值
atomic.CompareAndSwapInt32(&i, 100, 200)//Load(读)
//读取过程中相当于我们Metux中的加锁解锁操作,不会发生死锁、变量值不同步问题
atomic.LoadInt32(&i)//Store(写)
atomic.StoreInt32(&i, 200)//高版本的go可以直接使用:
atomic.Load(var_p)
atomic.Store(var_p)

示例代码:

func myAtomic() {//实现域Metux一样的操作var (x  int32 = 10wg sync.WaitGroup)var Add = func() {wg.Done()atomic.AddInt32(&x, 3)fmt.Println("Add 1")}var Sub = func() {wg.Done()atomic.AddInt32(&x, -2)fmt.Println("Sub 1")}for i := 0; i < 5; i++ {wg.Add(1)go Add()wg.Add(1)go Sub()}wg.Wait()fmt.Println("结束", x)
}

Select

是Go中的⼀个控制结构,类似于switch语句,select会监听case语句中channel的读写操作,当case可读写的时候,就执行case,进行读写操作以及case里面写的语句,当然select是随机选择的

default是当case当中都没有可读写的channel了,就会执行default中的语句

注意事项:

select中的case语句必须是⼀个channel操作
多个case都,select会随机选出⼀个执⾏
`没有可运⾏的case语句,且没有default语句,select将会阻塞,直到某个case通信可以运⾏`

需求:

创建不同类型的chan通道,写入数据后通过case对他进行分类别读取。

示例代码理解最快:

func select_case() {var (wg        sync.WaitGroupd_int     = make(chan int)d_string  = make(chan string)d_float64 = make(chan float64))go func() {wg.Add(1) //进入一个go协程+1d_int <- 123d_int <- 456d_string <- "string1"d_string <- "string2"d_float64 <- 123.456//养成好习惯,如果你不用通道了及时关闭,否则后面遍历处理数据的时候就会出错close(d_int)close(d_string)close(d_float64)}()wg.Wait() //等待for {select {case r := <-d_int:fmt.Println("int:", r)case r := <-d_string:fmt.Println("string:", r)case r := <-d_float64:fmt.Println("float64:", r)default:fmt.Println("default")}time.Sleep(1 * time.Second)}
}

Ticker定时器

创建定时器后,会向定时器的管道中按照指定的周期时间,定期写入事件,所以当你在周期到了的时候读取数据就不会阻塞,你读取完之后,还没到周期时间执行就会一直阻塞。

go的定时器记住三个函数

time.NewTicker(time.Second * 1) //创建一个周期性定时器
ticker.C   //ticker通道,Ticker对外仅暴露一个channel
ticker.Stop()  //停止ticker

示例代码

func myTicker() {ticker := time.NewTicker(1 * time.Second)count := 1for _ = range ticker.C {fmt.Println("执行了:", count)count++if count >= 10 {ticker.Stop()break}}
}

控制并发数量

核心机制

这里只是教你如何控制并发数量的例子,并不一定要按照我的方法来。

  1. sem <- data 的作用
    • sem 是一个带缓冲的通道,缓冲区大小为 workers,即 sem := make(chan 类型, workers)
    • 当通道的缓冲区已满时,发送操作(sem <-)会阻塞,等待通道有空间释放后再继续
    • 如果通道没有满,发送会立即成功。
  2. <-sem 的作用
    • defer func() { <-sem }() 会在协程完成任务后,从通道中取出一个值,释放空间。
    • 释放的空间让之前阻塞的发送操作可以继续执行,从而不会导致程序崩溃或死锁。
//这里是端口扫描的代码,提前搬上来了解一下控制并发数量
func start_WaitGroup_scan_port() {var (wg      sync.WaitGroupch      = make(chan int, 1024) // 增加缓冲区,减少阻塞count   intworkers = 100 // 控制并发数)var scanPort = func(hostname string, port int) {defer wg.Done()address := fmt.Sprintf("%s:%d", hostname, port)conn, err := net.DialTimeout("tcp", address, 2*time.Second)if err == nil {conn.Close()ch <- port}}// 控制并发数,很关键的理解sem := make(chan int, workers)for i := 0; i < 65536; i++ {wg.Add(1)sem <- 1go func(port int) {defer func() { <-sem }()scanPort("127.0.0.1", port)}(i)}go func() {wg.Wait()close(ch)}()for port := range ch {fmt.Printf("open: %d\n", port)count++}fmt.Printf("------------Open ports: %d-------------\n", count)fmt.Println("------------Scan——Done------------")
}

并发编程阶段练习

重要的细节

wg.Wait() 移到一个匿名协程中处理,并在 wg.Wait() 之后关闭通道,保证了以下两点:

  1. 通道关闭时,没有协程向通道写数据
    • 主协程从通道 rep 中读取数据,不负责关闭通道。
    • 匿名协程负责等待所有任务完成并关闭通道,确保通道关闭与写入互相隔离。
  2. 避免主协程被抢占的死锁风险
    • 如果你直接在主协程中调用 wg.Wait()close(rep),主协程在执行时可能与其他协程抢占资源,导致协程无法按时完成。
    • 匿名协程中执行 wg.Wait() 独立于主协程,不受读取数据或其他逻辑的影响。

可以看到下面中

go func() {wg.Wait()close(rep)
}()

这里原本我是直接放在主函数使用,开一个匿名协程去执行的,但是gpt解释了一下
就是:不能够在主函数中直接等待与关闭,而是需要在go协程中进行,因为直接在主函数wait后就close的话会导致go协程中还有正在执行的,这就很矛盾了,我们的wait命名是等待,但是不要忘记了他其实是Add函数去判断是否还需要等待,如果你提前Done了,主函数会立即抢占,那么这时候你的go协程其实还有很多代码没有执行完成就被抢占回去了,然后后面就是你要进行close,那么后续的go协程write相关操作就会报错了

func start_WaitGroup_scan_port() {var (wg    sync.WaitGroupcount int = 0rep       = make(chan bool))var WaitGroup_scan_port = func(hostname string, port int) {address := fmt.Sprintf("%s:%d", hostname, port)             //ip端口conn, err := net.DialTimeout("tcp", address, 2*time.Second) //2秒if err != nil {wg.Done()rep <- false} else {conn.Close()fmt.Printf("open: %d\n", port)wg.Done()rep <- true}}for i := 0; i <= 65535; i++ {wg.Add(1)go WaitGroup_scan_port("127.0.0.1", i)}go func() {wg.Wait()close(rep)}()for v := range rep {if v {count++}}fmt.Printf("------------Open ports: %d-------------\n", count)fmt.Println("------------Scan——Done------------")
}

端口扫描

1.使用WaitGroup进行并发,需要控制并发数量,可能会因为资源不够而导致结果不准确、不完整。

func start_WaitGroup_scan_port() {var (wg      sync.WaitGroupch      = make(chan int, 1024) // 增加缓冲区,减少阻塞count   intworkers = 100 // 控制并发数)var scanPort = func(hostname string, port int) {defer wg.Done()address := fmt.Sprintf("%s:%d", hostname, port)conn, err := net.DialTimeout("tcp", address, 2*time.Second)if err == nil {conn.Close()ch <- port}}// 控制并发数sem := make(chan int, workers)for i := 0; i < 65536; i++ {wg.Add(1)sem <- 1go func(port int) {defer func() { <-sem }()scanPort("127.0.0.1", port)}(i)}go func() {wg.Wait()close(ch)}()for port := range ch {fmt.Printf("open: %d\n", port)count++}fmt.Printf("------------Open ports: %d-------------\n", count)fmt.Println("------------Scan——Done------------")
}

2.使用通道,即单协程进行端口扫描

func start_Ch_scan_port() {var (ch        = make(chan int)count int = 0)var Ch_scan_port = func(hostname string) {defer close(ch)for i := 0; i < 65535; i++ {//扫描到开放端口放到ch通道中即可,主函数一直等待读取通道信息address := fmt.Sprintf("%s:%d", hostname, i)                //ip端口conn, err := net.DialTimeout("tcp", address, 2*time.Second) //2秒if err == nil {conn.Close()ch <- i}}runtime.Goexit()}go Ch_scan_port("127.0.0.1")runtime.Gosched() //等待go协程执行完for i := range ch {fmt.Printf("open: %d\n", i)count++}fmt.Printf("------------Open ports: %d-------------\n", count)fmt.Println("------------Scan——Done------------")
}

股票监控

这一题中能够领悟到挺多东西。

1.管道的读取数据,一般是不同协程之间进行读写操作,所以我们需要对读或者写分开协程操作
2.go协程之间需要不断对一个数据进行操作的话,一般要开一个通道然后通过对通道进行读写操作

最终效果

image-20241120003644836

示例代码:

package mainimport ("fmt""math/rand""sync/atomic""time"
)var price atomic.Value           //股票价格
var priceCh = make(chan float64) //股票通道,读写之间的一个通道
var stopCh = make(chan bool)     //开关监控
// 模拟股票涨跌(随机)
func randGP() float64 {return rand.Float64()*10 + 100 //控制在110之间
}// ticker 定时获取股票价格。模拟监控股票,且变更十次
func myTicker() {ticker := time.NewTicker(time.Second * 1)for {select {case <-ticker.C:priceCh <- randGP()case <-stopCh:fmt.Println("结束监控。")return}}
}
func update_g() {for i := range priceCh {price.Store(i)fmt.Printf("股票更新为:%.2f\n", i)}
}
func main() {go myTicker()go update_g()time.Sleep(5 * time.Second) // 监控5秒close(stopCh)               // 结束监控,close后stopCh会等于0,case能够读取//获取最终股票价格fmt.Printf("最终股票价格为:%.2f\n", price.Load())
}

相关文章:

Go红队开发—并发编程

文章目录 并发编程go协程chan通道无缓冲通道有缓冲通道创建⽆缓冲和缓冲通道 等协程sync.WaitGroup同步Runtime包Gosched()Goexit() 区别 同步变量sync.Mutex互斥锁atomic原子变量 SelectTicker定时器控制并发数量核心机制 并发编程阶段练习重要的细节端口扫描股票监控 并发编程…...

Oracle 导出所有表索引的创建语句

在Oracle数据库中&#xff0c;导出所有表的索引创建语句通常涉及到使用数据字典视图来查询索引的定义&#xff0c;然后生成对应的SQL语句。你可以通过查询DBA_INDEXES或USER_INDEXES视图&#xff08;取决于你的权限和需求&#xff09;来获取这些信息。 使用DBA_INDEXES视图 如…...

使用Docker方式一键部署MySQL和Redis数据库详解

一、前言 数据库是现代应用开发中不可或缺的一部分&#xff0c;MySQL和Redis作为两种广泛使用的数据库系统&#xff0c;分别用于关系型数据库和键值存储。本文旨在通过Docker和Docker Compose的方式&#xff0c;提供一个简洁明了的一键部署方案&#xff0c;确保数据库服务的稳…...

2020年蓝桥杯Java B组第二场题目+部分个人解析

#A&#xff1a;门牌制作 624 解一&#xff1a; public static void main(String[] args) {int count0;for(int i1;i<2020;i) {int ni;while(n>0) {if(n%102) {count;}n/10;}}System.out.println(count);} 解二&#xff1a; public static void main(String[] args) {…...

[深度学习] 大模型学习2-提示词工程指北

在文章大语言模型基础知识里&#xff0c;提示词工程&#xff08;Prompt Engineering&#xff09;作为大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;应用构建的一种方式被简要提及&#xff0c;本文将着重对该技术进行介绍。 提示词工程就是在和LLM聊…...

FPGA之硬件设计笔记-持续更新中

目录 1、说在前面2、FPGA硬件设计总计说明3、 原理图详解 - ARITX - 7 系列3.1 顶层框图介绍3.2 FPGA 电源sheet介绍&#xff1a;3.2.1 bank 14 和 bank 15的供电3.2.2 bank 0的供电3.2.3 Bank34 35 的供电 3.3 核电压和RAM电压以及辅助电压 4 原理图详解-- Ultrascale ARTIX4.…...

vue cli 与 vite的区别

1、现在我们一般会用vite来构建vue3的项目。 2、之前一开始的时候&#xff0c;我们会用vue cli的vue create来构建项目。 3、它们之间有什么区别呢&#xff1f; 1. 设计理念 Vue CLI&#xff1a; 是 Vue.js 官方提供的命令行工具&#xff0c;主要用于快速搭建 Vue 项目。 提…...

怎么在本地环境安装yarn包

一、安装Yarn的前置条件 安装Node.js和npm Yarn依赖于Node.js环境&#xff0c;需先安装Node.js官网的最新稳定版&#xff08;建议≥16.13.0&#xff09;。安装时勾选“Add to PATH”以自动配置环境变量。 二、安装Yarn的多种方式 1. 通过npm全局安装&#xff08;通用&#xf…...

【大模型】AI 辅助编程操作实战使用详解

目录 一、前言 二、AI 编程介绍 2.1 AI 编程是什么 2.1.1 为什么需要AI辅助编程 2.2 AI 编程主要特点 2.3 AI编程底层核心技术 2.4 AI 编程核心应用场景 三、AI 代码辅助编程解决方案 3.1 AI 大模型平台 3.1.1 AI大模型平台代码生成优缺点 3.2 AI 编码插件 3.3 AI 编…...

react18自定义hook实现

概念&#xff1a;自定义 hook 是一种将组件逻辑提取到可复用函数中的方式&#xff0c;它允许你在多个组件中共享相同的状态和行为。自定义 hook 的本质上是一个普通的 JavaScript 函数&#xff0c;它可以使用 React 内部的 hook&#xff08;如 useState、useEffect、useContext…...

一周学会Flask3 Python Web开发-Jinja2模板过滤器使用

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 在Jinja2中&#xff0c;过滤器(filter)是一些可以用来修改和过滤变量值的特殊函数&#xff0c;过滤器和变量用一个竖线 | &a…...

使用PDFMiner.six解析PDF数据

PDF&#xff08;可移植文档格式&#xff09;文件是由Adobe创建的一种灵活的文件格式&#xff0c;它允许文档在不同的软件、硬件和操作系统中一致地显示。每个PDF文件都包含对固定布局文档的全面描述&#xff0c;包括文本、字体、图形和其他必要的显示元素。pdf通常用于文档共享…...

本地svn

参考补充&#xff1a;https://blog.csdn.net/hhl_work/article/details/107832414 先在D:\coding_cangku下新建空文件夹&#xff0c;例&#xff1a;code1【类似gitee线上仓库】点击进入code1&#xff0c;右键选择TortoiseSVN&#xff0c;再下一级菜单下点击Create repository …...

金融支付行业技术侧重点

1. 合规问题 第三方支付系统的平稳运营&#xff0c;严格遵循《非银行支付机构监督管理条例》的各项条款是基础与前提&#xff0c;其中第十八条的规定堪称重中之重&#xff0c;是支付机构必须牢牢把握的关键准则。 第十八条明确指出&#xff0c;非银行支付机构需构建起必要且独…...

axios几种请求类型的格式

Axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;广泛用于浏览器和 Node.js 中发送 HTTP 请求。它支持多种请求格式&#xff0c;包括 GET、POST、PUT、DELETE 等。也叫RESTful 目录 一、axios几种请求类型的格式 1、get请求 2、post请求 3、put请求 4、delete请求 二…...

二、IDE集成DeepSeek保姆级教学(使用篇)

各位看官老爷好&#xff0c;如果还没有安装DeepSeek请查阅前一篇 一、IDE集成DeepSeek保姆级教学(安装篇) 一、DeepSeek在CodeGPT中使用教学 1.1、Edit Code 编辑代码 选中代码片段 —> 右键 —> CodeGPT —> Edit Code, 输入自然语言可编辑代码&#xff0c;点击S…...

通过理解 sk_buff 深入掌握 Linux 内核自定义协议族的开发实现

要开发 Linux 内核中的自定义协议族(如私有传输层或网络层协议),需基于 sk_buff 的结构和操作,结合内核网络栈的扩展机制。以下是实现这一目标的分步指南: 1. 协议族开发的核心步骤 (1) 注册自定义协议族 定义协议号 在 <linux/if_ether.h> 或自定义头文件中分配唯…...

Qt 自带颜色属性

Qt 系统自带颜色如下&#xff1a; enum GlobalColor {color0,color1,black,white,darkGray,gray,lightGray,red,green,blue,cyan,magenta,yellow,darkRed,darkGreen,darkBlue,darkCyan,darkMagenta,darkYellow,transparent};对应颜色如下&#xff1a; color0: 这是自定义颜色…...

Linux的文件与目录管理

rm -rf / 列出目录内容和属性 命令&#xff1a;ls 格式&#xff1a;ls 选项 文件名 例&#xff1a; ls -a 打印工作路径 命令&#xff1a;pwd 切换工作目录 命令&#xff1a;cd 格式&#xff1a;cd 相对路径或者绝对路径 查看文件类型 命令&#xff1a;file 格式…...

常用的 pip 命令

pip 是 Python 的包管理工具&#xff0c;可用于安装、卸载、更新和管理 Python 包。以下是一些常用的 pip 命令&#xff1a; 1. 安装包 安装最新版本的包 pip install package_namepackage_name 是你要安装的 Python 包的名称&#xff0c;例如 pip install requests 可以安装…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化

是不是受够了安装了oracle database之后sqlplus的简陋&#xff0c;无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话&#xff0c;配置.bahs_profile后也能解决上下翻页这些&#xff0c;但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可&#xff0c…...

使用SSE解决获取状态不一致问题

使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件&#xff0c;这个上传文件是整体功能的一部分&#xff0c;文件在上传的过程中…...