go语言进阶与依赖管理

87 阅读5分钟

并发与并行

image-20241109200529890.png

并发(Concurrency)

并发指的是在同一时间间隔内,多个操作可以“同时”进行。在并发编程中,多个任务可能会交替执行,给人一种它们同时进行的错觉。实际上,这些任务是在单个或多个处理器上轮流执行的,每个任务在执行时都拥有处理器的全部或部分资源。并发的关键点在于任务之间的切换足够快,以至于用户感觉它们是同时发生的。

特点

  • 多个任务在逻辑上是同时进行的。
  • 任务之间可能需要同步和通信。
  • 可以在单核或多核处理器上实现。

并行(Parallelism)

并行指的是在同一时刻,多个操作真正地同时进行。在并行编程中,多个任务可以在同一时间点上在不同的处理器上执行。这意味着每个任务都在不同的处理器上运行,它们可以独立于其他任务并行执行。

特点

  • 多个任务在物理上是同时进行的。
  • 任务之间通常不需要同步和通信(或者需要的较少)。
  • 只能在多核处理器上实现。

区别

  • 时间共享 vs. 空间共享:并发是时间共享的概念,任务在时间上交替执行;并行是空间共享的概念,任务在空间上(即不同的处理器上)同时执行。
  • 资源利用:并行可以更有效地利用多核处理器的资源,因为它允许多个任务同时在不同的处理器上执行。
  • 复杂性:并行编程通常比并发编程更复杂,因为它涉及到数据的分割、任务的分配以及结果的合并等。

1.1 Goroutine

image-20241109200713187.png

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

image-20241109202016730.png

第二种方式,需要加锁

提倡第一种方式

1.3 Channel

image-20241109202212286.png

第一种导致通信的同步

第二种则有缓冲

func CalSquare() {
    src := makechan int)
    dest := makechan 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

image-20241109204246697.png

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

  1. 设置代理: 使用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 成为官方推荐的依赖管理工具,取代了之前的 depglide 等第三方工具。以下是一些常用的 go mod 命令和它们的用途:

  1. 初始化模块

    go mod init <module-name>
    

    这条命令会创建一个新的模块,并在当前目录下生成 go.mod 文件,<module-name> 是模块的名称。

  2. 添加依赖

    go get <package>
    

    这条命令会添加指定的包作为依赖,并更新 go.mod 文件。

  3. 下载依赖

    go mod download
    

    这条命令会下载所有需要的依赖包到本地缓存。

  4. 查看模块依赖图

    go mod graph
    

    这条命令会显示当前模块的依赖图。

  5. 查看模块要求

    go mod require <module>@<version>
    

    这条命令会添加或更新 go.mod 文件中的模块版本要求。

  6. 替换模块

    go mod edit -replace <old>=<new>
    

    这条命令可以在 go.mod 文件中替换一个模块的路径。

  7. 同步模块

    go mod tidy
    

    这条命令会添加缺失的依赖和移除未使用的依赖,使 go.modgo.sum 文件保持最新。

  8. 验证依赖

    go mod verify
    

    这条命令会验证 go.sum 文件中的依赖是否与缓存中的一致。

  9. 查看模块信息

    go mod info
    

    这条命令会显示当前模块的信息。

  10. 为什么需要这个依赖

    go mod why <module>
    

    这条命令会显示为什么需要某个特定的依赖。

使用 go mod 可以有效地管理 Go 项目的依赖,确保依赖的一致性和安全性。通过 go.sum 文件,go mod 还能够提供依赖的完整性校验