go并发和并行
进程和线程
进程(Process)就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位,进程是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间。一个进程至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。
通俗的讲进程就是一个正在执行的程序。
线程 是进程的一个执行实例,是程序执行的最小单元,它是比进程更小的能独立运行的基本单位一个进程可以创建多个线程,同一个进程中的多个线程可以并发执行,一个程序要运行的话至少有一个进程。
并行和并发
并发:多个线程同时竞争一个位置,竞争到的才可以执行,每一个时间段只有一个线程在执行。
并行:多个线程可以同时执行,每一个时间段,可以有多个线程同时执行。
通俗的讲多线程程序在单核 CPU 上面运行就是并发,多线程程序在多核 CUP 上运行就是并行,如果线程数大于 CPU 核数,则多线程程序在多个 CPU 上面运行既有并行又有并发。
协程(goroutine)和主线程
golang 中的主线程:(可以理解为线程/也可以理解为进程),在一个 Golang 程序的主线程上可以起多个协程。Golang 中多协程可以实现并行或者并发。
协程:可以理解为用户级线程,这是对内核透明的,也就是系统并不知道有协程的存在,是完全由用户自己的程序进行调度的。Golang 的一大特色就是从语言层面原生支持协程,在函数或者方法前面加 go 关键字就可创建一个协程。可以说 Golang 中的协程就是goroutine 。
Golang 中的多协程有点类似其他语言中的多线程。
多协程和多线程:Golang 中每个 goroutine (协程) 默认占用内存远比 Java 、C 的线程少。OS 线程(操作系统线程)一般都有固定的栈内存(通常为 2MB 左右),一个 goroutine (协程) 占用内存非常小,只有 2KB 左右,多协程 goroutine 切换调度开销方面远比线程要少。
这也是为什么越来越多的大公司使用 Golang 的原因之一。
Goroutine 的使用
先看一个简单的并行执行需求:
在主线程(可以理解成进程)中,开启一个 goroutine, 该协程每隔 50 毫秒秒输出 “你好 golang” 在主线程中也每隔 50 毫秒输出"你好 golang", 输出 10 次后,退出程序,要求主线程和goroutine 同时执行。
实现代码:
package mainimport ("fmt""strconv""time"
)func test() {for i := 1; i <= 10; i++ {fmt.Println("test () hello,world " + strconv.Itoa(i))time.Sleep(time.Second)}
}
func main() {go test() // 开启了一个协程for i := 1; i <= 10; i++ {fmt.Println(" main() hello,golang" + strconv.Itoa(i))time.Sleep(time.Second)}
}
上面代码看上去没有问题,但是要注意主线程执行完毕后即使协程没有执行完毕,程序会退出,所以我们需要对上面代码进行改造。
sync.WaitGroup 可以实现主线程等待协程执行完毕。
package mainimport ("fmt""strconv""sync""time"
)var wg sync.WaitGroup //1、定义全局的 WaitGroup
func test() {for i := 1; i <= 10; i++ {fmt.Println("test () 你好 golang " + strconv.Itoa(i))time.Sleep(time.Millisecond * 50)}wg.Done() // 4、goroutine 结束就登记-1
}
func main() {wg.Add(1) //2、启动一个 goroutine 就登记+1go test()for i := 1; i <= 2; i++ {fmt.Println(" main() 你好 golang" + strconv.Itoa(i))time.Sleep(time.Millisecond * 50)}wg.Wait() // 3、等待所有登记的 goroutine 都结束
}
运行结果:
main() 你好 golang1
test () 你好 golang 1
test () 你好 golang 2main() 你好 golang2
test () 你好 golang 3
test () 你好 golang 4
test () 你好 golang 5
test () 你好 golang 6
test () 你好 golang 7
test () 你好 golang 8
test () 你好 golang 9
test () 你好 golang 10
启动多个 Goroutine
在 Go 语言中实现并发就是这样简单,我们还可以启动多个 goroutine。让我们再来一个例子:
(这里使用了 sync.WaitGroup 来实现等待 goroutine 执行完毕)
package mainimport ("fmt""sync"
)var wg sync.WaitGroupfunc hello(i int) {defer wg.Done() // goroutine 结束就登记-1fmt.Println("Hello Goroutine!", i)
}
func main() {for i := 0; i < 10; i++ {wg.Add(1) // 启动一个 goroutine 就登记+1go hello(i)}wg.Wait() // 等待所有登记的 goroutine 都结束
}
多次执行上面的代码,会发现每次打印的数字的顺序都不一致。这是因为 10 个 goroutine是并发执行的,而 goroutine 的调度是随机的。
设置 Golang 并行运行的时候占用的cpu数量
Go 运行时的调度器使用 GOMAXPROCS 参数来确定需要使用多少个 OS 线程来同时执行 Go代码。默认值是机器上的 CPU 核心数。例如在一个 8 核心的机器上,调度器会把 Go 代码同时调度到 8 个 OS 线程上。
Go 语言中可以通过 runtime.GOMAXPROCS()函数设置当前程序并发时占用的 CPU 逻辑核心数。
Go1.5 版本之前,默认使用的是单核心执行。Go1.5 版本之后,默认使用全部的 CPU 逻辑核心数。
package mainimport ("fmt""runtime"
)func main() {//获取当前计算机上面的 Cup 个数cpuNum := runtime.NumCPU()fmt.Println("cpuNum=", cpuNum)//可以自己设置使用多个 cpuruntime.GOMAXPROCS(cpuNum - 1)fmt.Println("ok")
}
Channel 管道
管道是 Golang 在语言级别上提供的 goroutine 间的通讯方式,我们可以使用 channel 在多个 goroutine 之间传递消息。如果说 goroutine 是 Go 程序并发的执行体,channel 就是它们之间的连接。channel 是可以让一个 goroutine 发送特定值到另一个 goroutine 的通信机制。
Golang 的并发模型是 CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。
Go 语言中的管道(channel)是一种特殊的类型。管道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个管道都是一个具体类型的导管,也就是声明 channel 的时候需要为其指定元素类型。
- channel 类型
channel 是一种类型,一种引用类型。声明管道类型的格式如下:
var 变量 chan 元素类型
举几个例子:
var ch1 chan int // 声明一个传递整型的管道
var ch2 chan bool // 声明一个传递布尔型的管道
var ch3 chan []int // 声明一个传递 int 切片的管道
- 创建channel
声明的管道后需要使用 make 函数初始化之后才能使用。
创建 channel 的格式如下:
make(chan 元素类型, 容量)
代码示例:
//创建一个能存储 10 个 int 类型数据的管道
ch1 := make(chan int, 10)
//创建一个能存储 4 个 bool 类型数据的管道
ch2 := make(chan bool, 4)
//创建一个能存储 3 个[]int 切片类型数据的管道
ch3 := make(chan []int, 3
- channel操作
管道有发送(send)、接收(receive)和关闭(close)三种操作。
发送和接收都使用<-符号。
现在我们先使用以下语句定义一个管道:
ch := make(chan int, 3)
- 发送(将数据放在管道内)
将一个值发送到管道中:
ch <- 10 // 把 10 发送到 ch 中
- 接收(从管道内取值)
从一个管道中接收值:
x := <- ch // 从 ch 中接收值并赋值给变量 x
<-ch // 从 ch 中接收值,忽略结果
- 关闭管道
我们通过调用内置的 close 函数来关闭管道:
close(ch)
关于关闭管道需要注意的事情是,只有在通知接收方 goroutine 所有的数据都发送完毕的时候才需要关闭管道。管道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束操作之后关闭文件是必须要做的,但关闭管道不是必须的。
关闭后的管道有以下特点:
(1)对一个关闭的管道再发送值就会导致 panic。
(2)对一个关闭的管道进行接收会一直获取值直到管道为空。
(3)对一个关闭的并且没有值的管道执行接收操作会得到对应类型的零值。
(4)关闭一个已经关闭的管道会导致 panic。
- 管道阻塞
- 无缓冲的管道
如果创建管道的时候没有指定容量,那么我们可以叫这个管道为无缓冲的管道
无缓冲的管道又称为阻塞的管道。我们来看一下下面的代码:
package mainimport ("fmt"
)func main() {ch := make(chan int)ch <- 10fmt.Println("发送成功")
}
上面这段代码能够通过编译,但是执行的时候会出现以下错误:
fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:
main.main()D:/project/go-project/main.go:10 +0x28Process finished with the exit code 2
- 有缓冲的管道
解决上面问题的方法还有一种就是使用有缓冲区的管道。我们可以在使用 make 函数初始化管道的时候为其指定管道的容量,例如:
package mainimport ("fmt"
)func main() {ch := make(chan int, 1) // 创建一个容量为 1 的有缓冲区管道ch <- 10fmt.Println("发送成功")
}
只要管道的容量大于零,那么该管道就是有缓冲的管道,管道的容量表示管道中能存放元素的数量。就像你小区的快递柜只有那么个多格子,格子满了就装不下了,就阻塞了,等到别人取走一个快递员就能往里面放一个。
管道阻塞具体代码如下:
package mainimport ("fmt"
)func main() {ch := make(chan int, 1)ch <- 10ch <- 12fmt.Println("发送成功")
}
显然由于ch <-12这行代码就会报错:
fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:
main.main()D:/project/go-project/main.go:10 +0x45Process finished with the exit code 2
解决办法:
package mainimport ("fmt"
)func main() {ch := make(chan int, 1)ch <- 10 //放进去<-ch //取走ch <- 12 //放进去<-ch //取走ch <- 17 //还可以放进去fmt.Println("发送成功")
}
- for range 从管道循环取值
当向管道中发送完数据时,我们可以通过 close 函数来关闭管道。
当管道被关闭时,再往该管道发送值会引发 panic,从该管道取值的操作会先取完管道中的值,再然后取到的值一直都是对应类型的零值。那如何判断一个管道是否被关闭了呢?
我们来看下面这个例子
package mainimport ("fmt"
)func main() {var ch1 = make(chan int, 5)for i := 0; i < 5; i++ {ch1 <- i + 1}close(ch1) //关闭管道//使用 for range 遍历管道,当管道被关闭的时候就会退出 for range,如果没有关闭管道 就会报个错误 fatal error: all goroutines are asleep - deadlock!//通过 for range 来遍历管道数据 管道没有 keyfor val := range ch1 {fmt.Println(val)}
}
运行结果:
1
2
3
4
5
从上面的例子中我们看到有两种方式在接收值的时候判断该管道是否被关闭,不过我们通常使用的是 for range 的方式。使用 for range 遍历管道,当管道被关闭的时候就会退出 for range。
Goroutine 结合 Channel 管道
需求 :
定义两个方法,一个方法给管道里面写数据,一个给管道里面读取数据。要求同步进行。
(1)开启一个 fn1 的的协程给向管道 inChan 中写入 100 条数据
(2)开启一个 fn2 的协程读取 inChan 中写入的数据
(3)注意:fn1 和 fn2 同时操作一个管道
(4)主线程必须等待操作完成后才可以退出
package mainimport ("fmt""sync""time"
)var wg sync.WaitGroupfunc fn1(intChan chan int) {for i := 0; i < 100; i++ {intChan <- i + 1fmt.Println("writeData 写入数据-", i+1)time.Sleep(time.Millisecond * 100)}close(intChan)wg.Done()
}
func fn2(intChan chan int) {for v := range intChan {fmt.Printf("readData 读到数据=%v\n", v)time.Sleep(time.Millisecond * 50)}wg.Done()
}
func main() {allChan := make(chan int, 100)wg.Add(1)go fn1(allChan)wg.Add(1)go fn2(allChan)wg.Wait()fmt.Println("读取完毕...")
}
单向管道
有的时候我们会将管道作为参数在多个任务函数间传递,很多时候我们在不同的任务函数中使用管道都会对其进行限制,比如限制管道在函数中只能发送或只能接收。
只写通道 (chan<-):只能向这个通道发送数据,不能从中接收数据。
只读通道 (<-chan):只能从这个通道接收数据,不能向其中发送数据。
package mainimport ("fmt"
)func main() {// 创建一个双向通道,并分配内存biChan := make(chan int, 3)// 只写通道var chan2 chan<- int = biChanchan2 <- 20fmt.Println("chan2=", chan2)// 向双向通道发送更多数据biChan <- 30// 只读通道var chan3 <-chan int = biChannum2 := <-chan3fmt.Println("num2", num2)
}
运行结果:
chan2= 0xc0000a6080
num2 20
select 多路复用
在某些场景下我们需要同时从多个通道接收数据。这个时候就可以用到 golang 中给我们提供的 select 多路复用。
通常情况通道在接收数据时,如果没有数据可以接收将会发生阻塞。
比如说下面代码来实现从多个通道接受数据的时候就会发生阻塞:
for{
// 尝试从 ch1 接收值
data, ok := <-ch1
// 尝试从 ch2 接收值
data, ok := <-ch2 …
}
这种方式虽然可以实现从多个管道接收值的需求,但是运行性能会差很多。为了应对这种场景,Go 内置了 select 关键字,可以同时响应多个管道的操作。
select 的使用类似于 switch 语句,它有一系列 case 分支和一个默认的分支。每个 case 会对应一个管道的通信(接收或发送)过程。select 会一直等待,直到某个 case 的通信操作完成时,就会执行 case 分支对应的语句。具体格式如下:
select{
case <-ch1:
... case data := <-ch2:
... case ch3<-data:
...
default:
默认操作
}
举个小例子来演示下 select 的使用:
package mainimport ("fmt""time"
)func main() {//1.定义一个管道 10 个数据 intintChan := make(chan int, 10)for i := 0; i < 10; i++ {intChan <- i}//2.定义一个管道 5 个数据 stringstringChan := make(chan string, 5)for i := 0; i < 5; i++ {stringChan <- "hello" + fmt.Sprintf("%d", i)}for {select {case v := <-intChan:fmt.Printf("从 intChan 读取的数据%d\n", v)case v := <-stringChan:fmt.Printf("从 stringChan 读取的数据%s\n", v)default:fmt.Printf("都取不到了,不玩了, 程序员可以加入逻辑\n")time.Sleep(time.Second)return}}}
Golang 并发安全和锁
- 互斥锁
互斥锁是传统并发编程中对共享资源进行访问控制的主要手段,它由标准库 sync 中的 Mutex结构体类型表示。sync.Mutex 类型只有两个公开的指针方法,Lock 和 Unlock。Lock 锁定当前的共享资源,Unlock 进行解锁。
我们先看一段有问题的代码:
package mainimport ("fmt""time"
)var count = 0func test() {count++fmt.Println("the count is : ", count)time.Sleep(time.Millisecond)
}
func main() {for r := 0; r < 100; r++ {go test()}time.Sleep(time.Second)
}
这段代码的主要问题是它涉及到并发编程中的竞态条件(race condition)。具体来说,多个goroutine同时访问和修改共享变量count,而没有适当的同步机制来保护对这个变量的访问。这会导致不可预测的行为,因为不同的goroutine可能会在同一时间尝试读取和写入count,从而导致数据竞争。
互斥锁解决这个问题:
package mainimport ("fmt""sync""time"
)var count = 0
var mu sync.Mutex // 声明一个互斥锁func test(wg *sync.WaitGroup) {defer wg.Done() // 确保在函数退出时调用Done()mu.Lock() // 获取锁count++fmt.Println("the count is : ", count)mu.Unlock() // 释放锁time.Sleep(time.Millisecond)
}func main() {var wg sync.WaitGroup // 声明一个WaitGroup用于等待所有goroutine完成for r := 0; r < 100; r++ {wg.Add(1) // 每启动一个goroutine就增加计数go test(&wg)}wg.Wait() // 等待所有goroutine完成
}
这时我们可以看到打印的结果就正常了。
使用互斥锁能够保证同一时间有且只有一个 goroutine 进入临界区,其他的 goroutine 则在等待锁;当互斥锁释放后,等待的 goroutine 才可以获取锁进入临界区,多个 goroutine 同时等待一个锁时,唤醒的策略是随机的。
虽然使用互斥锁能解决资源争夺问题,但是并不完美,通过全局变量加锁同步来实现通讯,并不利于多个协程对全局变量的读写操作。这个时候我们也可以通过另一种方式来实现上面的功能管道(Channel)。
- 读写锁
互斥锁的本质是当一个 goroutine 访问的时候,其他 goroutine 都不能访问。这样在资源同步,避免竞争的同时也降低了程序的并发性能。程序由原来的并行执行变成了串行执行。
其实,当我们对一个不会变化的数据只做“读”操作的话,是不存在资源竞争的问题的。因为数据是不变的,不管怎么读取,多少 goroutine 同时读取,都是可以的。
所以问题不是出在“读”上,主要是修改,也就是“写”。修改的数据要同步,这样其他goroutine 才可以感知到。所以真正的互斥应该是读取和修改、修改和修改之间,读和读是没有互斥操作的必要的。
因此,衍生出另外一种锁,叫做读写锁。
读写锁可以让多个读操作并发,同时读取,但是对于写操作是完全互斥的。也就是说,当一个 goroutine 进行写操作的时候,其他 goroutine 既不能进行读操作,也不能进行写操作。
GO 中的读写锁由结构体类型 sync.RWMutex 表示。此类型的方法集合中包含两对方法:
一组是对写操作的锁定和解锁,简称“写锁定”和“写解锁”
func (*RWMutex)Lock()
func (*RWMutex)Unlock()
另一组表示对读操作的锁定和解锁,简称为“读锁定”与“读解锁”:
func (*RWMutex)RLock()
func (*RWMutex)RUnlock()
读写锁示例代码:
package mainimport ("fmt""sync""time"
)var count int
var mutex sync.RWMutex
var wg sync.WaitGroup// 写的方法
func write() {mutex.Lock()fmt.Println("执行写操作")time.Sleep(time.Second * 3)mutex.Unlock()wg.Done()
}// 读的方法
func read() {mutex.RLock()fmt.Println("执行读操作")time.Sleep(time.Second * 3)mutex.RUnlock()wg.Done()
}
func main() {// 开启 10 个协程执行写操作for i := 0; i < 10; i++ {wg.Add(1)go read()}//开启 10 个协程执行读操作for i := 0; i < 10; i++ {wg.Add(1)go write()}wg.Wait()
}
Goroutine Recover 解决协程中出现的 Panic
package mainimport ("fmt""time"
)// 函数
func sayHello() {for i := 0; i < 10; i++ {time.Sleep(time.Second)fmt.Println("hello,world")}
}// 函数
func test() {//这里我们可以使用 defer + recoverdefer func() {//捕获 test 抛出的 panicif err := recover(); err != nil {fmt.Println("test() 发生错误", err)}}()//定义了一个 mapvar myMap map[int]stringmyMap[0] = "golang" //error
}func main() {sayHello()test()
}
参考文献
https://gobyexample.com/
https://www.w3schools.com/go/
https://go.dev/doc/tutorial/
https://www.geeksforgeeks.org/golang-tutorial-learn-go-programming-language/
相关文章:

go并发和并行
进程和线程 进程(Process)就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位,进程是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间。…...

一种解决SoC总线功能验证完备性的技术
1. 前言 通过总线将各个IP通过总线连接起来的SoC芯片是未来的大趋势,也是缩短芯片开发周期,抢先进入市场的常用方法。如何确保各个IP是否正确连接到总线上,而且各IP的地址空间分配是否正确,是一件很棘手的事情。本文提出了一种新…...

Web3 与区块链:开启透明、安全的网络新时代
在这个信息爆炸的时代,我们对网络的透明性、安全性和隐私保护的需求日益增长。Web3,作为新一代互联网的代表,正携手区块链技术,引领我们走向一个更加透明、安全和去中心化的网络世界。本文将深入探讨 Web3 的基本概念、区块链技术…...
c#中Thread.Join()方法的经典示例
在 C# 中,Thread.Join 是一个非常有用的方法,它可以让主线程(调用线程)等待子线程(被调用线程)执行完毕后再继续执行。 1、经典示例1 using System; using System.Threading;public class Example {stati…...

深入了解越权漏洞:概念、危害与防范
前言 越权漏洞作为一种常见且极具威胁的安全隐患,就像隐藏在暗处的 “黑客帮凶”,时刻威胁着我们的数据安全和隐私。就让我们一起揭开越权漏洞的神秘面纱,深入了解它的来龙去脉、危害以及应对之策。 一、什么是越权漏洞 想象一下࿰…...

MySQL 数据库编程-C++
目录 1 数据库基本知识 1.1 MYSQL常见命令 1.2 SQL注入 1.3 ORM框架 1 数据库基本知识 MySQL 为关系型数据库(Relational Database Management System), 这种所谓的"关系型"可以理解为"表格"的概念, 一个关系型数据库由一个或数个表格组成:…...

dl学习笔记(9):pytorch数据处理的完整流程
1)自动导入常用库的设置方式 在开始之前,这里介绍一下自动导入常用的你需要加载的库的操作方式。 首先在我们的目录下找到ipython文件,如下图: 然后找到里面的startup文件: 然后新建一个文本文档,输入你每…...

wps中的vba开发
推荐先学习vba语言(兰色幻想80集) 保存代码时注意保存为 .xlsm(启用宏的工作簿) 子程序SUN和函数FUNCTION: Sub 第一个程序()MsgBox "这是第一个程序"End Sub 注释Sub 第二个程序()Dim str As Stringstr "这是第二个程序&…...
力扣 LCR 078 合并K个升序链表
思路 解题过程 分治合并 与 LeetCode 21题 合并两个有序链表 相似 只是在此题的基础上增加了链表的数量。 使用递归将链表数组不断分成两半,直到分成的小组都只剩下一个链表元素为止,随后开始合并链表。 复杂度 时间复杂度: O(N * logK) K 为 链表(li…...

【hive】记一次hiveserver内存溢出排查,线程池未正确关闭导致
一、使用 MemoryAnalyzer软件打开hprof文件 很大有30G,win内存24GB,不用担心可以打开,ma软件能够生成索引文件,逐块分析内存,如下图。 大约需要4小时。 overview中开不到具体信息。 二、使用Leak Suspects功能继续…...
React Native 开发 安卓项目构建工具Gradle的配置和使用
gradle基本知识 gradle是React Native和Flutter调试、构建安卓App的打包工具。 gradle可以简单的类比为前端的webpack,webpack将源文件打包成HTML、CSS、JavaScript,而gradle将源文件打包成apk或aar(Android Assemble Bundle)。…...

IntelliJ IDEA新版本的底部version control(或git)里local change窗口显示配置修改详细教程
环境 IntelliJ IDEA 2024.3.2.2 (Ultimate Edition) Runtime version: 21.0.58-b631.30 aarch64 (JCEF 122.1.9) macOS 14.2.1 自己记录下。 步骤 设置 --> Version Control(版本控制) --> Commit(提交) --> 去掉勾选:Use non-modal commit interface…...

MySQL三大日志——binlog、redoLog、undoLog详解
日志是mysql数据库的重要组成部分,记录着数据库运行期间各种状态信息,能帮助我们进行很多容错及分析工作,其中有三大日志与我们这些开发者息息相关,本文将介绍binlog、redoLog、undoLog三种日志: 1. redoLog 1.1 为什么…...

MCU应用踩坑笔记(ADC 中断 / 查询法)
问题描述 IC:SC92F7596,在使用过程中,发现一个问题,就是我们使用到了ADC功能,程序的代码如下: ADC采样周期200ms ,采样个数:4 在使用过程中,因配置了ADC中断使能,在中断…...
32.日常算法
1.最大子数组和 题目来源 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。 示例 1: 输入:nums [-2,1,-3,4,-1,2,1,…...

通过docker安装部署deepseek以及python实现
前提条件 Docker 安装:确保你的系统已经安装并正确配置了 Docker。可以通过运行 docker --version 来验证 Docker 是否安装成功。 网络环境:保证设备有稳定的网络连接,以便拉取 Docker 镜像和模型文件。 步骤一:拉取 Ollama Docker 镜像 Ollama 可以帮助我们更方便地管理…...
批量提取word表格数据到一个excel
新建一个excel到word同级目录altf11打开vba窗口并新建模块粘贴下方代码(修改一些必要参数)回到excel表格界面,altf8选择执行该宏注意要在信任中心开启运行vba宏 Sub 批量提取word表格数据到excel()Dim wdApp As Object, wdDoc As ObjectDim …...
使用 Axios 获取用户数据并渲染——个人信息设置
目录 1. HTML 部分(前端页面结构) HTML 结构解析: 2. JavaScript 部分(信息渲染逻辑) JavaScript 解析: 3. 完整流程 4. 总结 5. 适用场景 本文将介绍如何通过 Axios 从服务器获取用户信息࿰…...

DeepSeek在FPGA/IC开发中的创新应用与未来潜力
随着人工智能技术的飞速发展,以DeepSeek为代表的大语言模型(LLM)正在逐步渗透到传统硬件开发领域。在FPGA(现场可编程门阵列)和IC(集成电路)开发这一技术密集型行业中,DeepSeek凭借其…...

【GitLab CI/CD 实践】从 0 到 1 搭建高效自动化部署流程
网罗开发 (小红书、快手、视频号同名) 大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等…...

YOLO在QT中的完整训练、验证与部署方案
以下是YOLO在QT中的完整训练、验证与部署方案: 训练方案 准备数据集: 收集数据:收集与目标检测任务相关的图像数据集,可以是公开数据集如COCO、Pascal VOC,也可以是自定义数据集。标注数据:使用标注工具如…...

网络寻路--图论
所以我们固定题中M条边(因为这M条一定联通) P8605 [蓝桥杯 2013 国 AC] 网络寻路 - 洛谷 #include<bits/stdc.h> using namespace std; #define N 100011 typedef long long ll; typedef pair<int,int> pii; int n,m; int d[N],u[N],v[N]…...

TomatoSCI数据分析实战:探索社交媒体成瘾
今天我们尝试对一份社交媒体成瘾的调查数据进行几项简单的分析,看看可以得出哪些有意思的结论?图1A是这份数据的说明,因为篇幅太长只把部分数据贴出来(图1B)。 01 不同性别的成瘾程度会不同吗? 我们使用bo…...

Ctrl+R 运行xxx.exe,发现有如下问题.
CtrlR 运行xxx.exe,发现有如下问题. (1)找不到Qt5Core.all,Qt5Cored.dll,Qt5Gui.dll,Qt5Guid.dll,Qt5Widgets.all,Qt5Widgetsd.dll? (2)之后找不到libwinpthread-1.dll 从这个目录拷贝相应的库到运行xx.exe目录下 方法二:将库路径添加到系统PATH环境变量里: 在Path中添加路…...

Reactor和Proactor
reactor的重要组件包括:Event事件、Reactor反应堆、Demultiplex事件分发器、Eventhandler事件处理器。...

网络安全-等级保护(等保) 3-3-1 GB/T 36627-2018 附录A (资料性附录) 测评后活动、附 录 B (资料性附录)渗透测试的有关概念说明
################################################################################ GB/T 36627-2018 《信息安全技术 网络安全等级保护测试评估技术指南》对网络安全等级保护测评中的相关测评技术进行明确的分类和定义,系统地归纳并阐述测评的技术方法,概述技术性安全测试和…...

【八股消消乐】MySQL参数优化大汇总
😊你好,我是小航,一个正在变秃、变强的文艺倾年。 🔔本专栏《八股消消乐》旨在记录个人所背的八股文,包括Java/Go开发、Vue开发、系统架构、大模型开发、具身智能、机器学习、深度学习、力扣算法等相关知识点ÿ…...
华为仓颉语言初识:并发编程之同步机制(上)
前言 线程同步机制是多线程下解决线程对共享资源竞争的主要方式,华为仓颉语言提供了三种常见的同步机制用来保证线程同步安全,分别是原子操作,互斥锁和条件变量。本篇文章详细介绍主要仓颉语言解决同步机制的方法,建议点赞收藏&a…...

Linux配置yum 时间同步服务 关闭防火墙 关闭ESlinux
1、配置yum 1.1、Could not resolve host: mirrorlist.centos.org; 未知的错误 https://blog.csdn.net/fansfi/article/details/146369946?fromshareblogdetail&sharetypeblogdetail&sharerId146369946&sharereferPC&sharesourceRockandrollman&sharefr…...

网页前端开发(基础进阶4--axios)
Ajax Ajax(异步的JavaScript和XML) 。 XML是可扩展标记语言,本质上是一种数据格式,可以用来存储复杂的数据结构。 可以通过Ajax给服务器发送请求,并获取服务器响应的数据。 Ajax采用异步交互:可以在不重新加载整个页面的情况下&am…...