并发与并行
并发(Concurrency)
并发指的是在同一时间间隔内,多个操作可以“同时”进行。在并发编程中,多个任务可能会交替执行,给人一种它们同时进行的错觉。实际上,这些任务是在单个或多个处理器上轮流执行的,每个任务在执行时都拥有处理器的全部或部分资源。并发的关键点在于任务之间的切换足够快,以至于用户感觉它们是同时发生的。
特点:
- 多个任务在逻辑上是同时进行的。
- 任务之间可能需要同步和通信。
- 可以在单核或多核处理器上实现。
并行(Parallelism)
并行指的是在同一时刻,多个操作真正地同时进行。在并行编程中,多个任务可以在同一时间点上在不同的处理器上执行。这意味着每个任务都在不同的处理器上运行,它们可以独立于其他任务并行执行。
特点:
- 多个任务在物理上是同时进行的。
- 任务之间通常不需要同步和通信(或者需要的较少)。
- 只能在多核处理器上实现。
区别
- 时间共享 vs. 空间共享:并发是时间共享的概念,任务在时间上交替执行;并行是空间共享的概念,任务在空间上(即不同的处理器上)同时执行。
- 资源利用:并行可以更有效地利用多核处理器的资源,因为它允许多个任务同时在不同的处理器上执行。
- 复杂性:并行编程通常比并发编程更复杂,因为它涉及到数据的分割、任务的分配以及结果的合并等。
1.1 Goroutine
go语言一次可创建上万次协程,故而适合高并发的开发
在调用函数时,在函名前加上go关键字,即可创建一个协程
func HelloGoRoutine() {
for i:=0;i<5;i++ {
go func(j int) {//在调用函数时,在函名前加上go关键字
hello(j)
}(i)
}
time.Sleep(time.Second)
}
该代码输出结果为乱序,可见运行过程是并行的
1.2 CSP
第二种方式,需要加锁
提倡第一种方式
1.3 Channel
第一种导致通信的同步
第二种则有缓冲
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3)
go func() {
defer close(src)
for i:=0;i<10;i++ {
src <- i
}
}()
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
//复杂操作
println(i)
}
}
可以看到按顺序输出结果,保证顺序性,并发安全
1.4 并发安全 Lock
模拟一个对同一内存区域的高并发操作,可以看到,在不对方法进行加锁操作时,最终结果为随机结果加锁保证结果正确,并发安全
sleep方法实现暴力阻塞
1.5 WaitGroup
func ManyGoWait() {
var wg sync.WaitGroup
wq.Add(5)
for i:=0;i<5;i++ {
go func(j int) {
defer wg.Done()
hello(j)
}(i)
}
wg.Wait()
}
依赖管理
2.1 依赖管理演进
GOPATH->GO VENDIR -> GO MODULE
Go Module 1.通过go.mod 文件管理依赖包版本 2.通过go get/go mod 指令工具管理依赖包
目标:定义版本规则和管理依赖关系
2.2 依赖管理三要素
1.配置文件,依赖描述 ——go.mod
2.中心仓库管理依赖库——Proxy
3.本地工具——go get/mod
2.3
1.依赖配置——go.mod
module example/project/app 依赖管理基本单元
go 1.16 原生库
require(
example/lib1 v1.0.2 单元依赖
example/lib2v1.0.0 // indirect
example/Lib3v0.1.0-20190725025543-5a5fe074e612
example/lib4v0.0.0-20180306012644-bacd9c7ef1dd // indirect
example/lib5/v3v3.0.2
example/lib6v3.2.0+incompatible
)
1.语义版本
V1.3.0 V2.3.0
2.commit 伪版本
vx.0.0-yyyymmddhhmmss-abcdefgh1234
v0.0.0-20220401081311-c38fb59326b7 v1.0.0-20201130134442-10cb98267c6c
2.依赖分发——Proxy
-
设置代理: 使用
go env命令设置GOPROXY环境变量,指定一个或多个代理服务器。例如:shell
go env -w GOPROXY=https://proxy.example.com,direct这里
https://proxy.example.com是代理服务器的地址,direct表示如果代理服务器无法访问模块,则直接从源服务器获取。
3. 本地工具
go mod 是 Go 语言从 1.11 版本开始引入的模块支持工具,用于管理项目的依赖关系。从 Go 1.16 版本开始,go mod 成为官方推荐的依赖管理工具,取代了之前的 dep 和 glide 等第三方工具。以下是一些常用的 go mod 命令和它们的用途:
-
初始化模块:
go mod init <module-name>这条命令会创建一个新的模块,并在当前目录下生成
go.mod文件,<module-name>是模块的名称。 -
添加依赖:
go get <package>这条命令会添加指定的包作为依赖,并更新
go.mod文件。 -
下载依赖:
go mod download这条命令会下载所有需要的依赖包到本地缓存。
-
查看模块依赖图:
go mod graph这条命令会显示当前模块的依赖图。
-
查看模块要求:
go mod require <module>@<version>这条命令会添加或更新
go.mod文件中的模块版本要求。 -
替换模块:
go mod edit -replace <old>=<new>这条命令可以在
go.mod文件中替换一个模块的路径。 -
同步模块:
go mod tidy这条命令会添加缺失的依赖和移除未使用的依赖,使
go.mod和go.sum文件保持最新。 -
验证依赖:
go mod verify这条命令会验证
go.sum文件中的依赖是否与缓存中的一致。 -
查看模块信息:
go mod info这条命令会显示当前模块的信息。
-
为什么需要这个依赖:
go mod why <module>这条命令会显示为什么需要某个特定的依赖。
使用 go mod 可以有效地管理 Go 项目的依赖,确保依赖的一致性和安全性。通过 go.sum 文件,go mod 还能够提供依赖的完整性校验