go语言进阶与依赖管理笔记 | 青训营

157 阅读1分钟

一.语言进阶

1.1 Goroutine(协程)

并发:多线程程序在一个核的cpu上运行

并行:多线程程序在多个核的cpu上运行

协程:用户态,轻量级线程,栈KB级别

线程:内核态,线程跑多个协程,栈MB级别

调用函数或者方法时,在前面加上关键字 go,可以让一个新的 Go 协程并发地运行。 一个go func(){}()开启一个子协程,for + go func(){}()开启多个子协程

func(){}是一个匿名函数

for i := 0; i < 5; i++ { go func(j int) { fmt.Println(j) }(i) }

如果你只看到了四个值而不是五个,这可能是因为主 Goroutine 在启动了五个 Goroutine 后就终止了,而其中一个或多个 Goroutine 没有足够的时间来完成打印操作。这是一个并发执行的情况,具体的输出结果可能因为 Goroutine 的调度和执行时间而有所不同。

主 Goroutine 与其他 Goroutine 之间没有本质上的区别。它们都是并发执行的独立任务,具有相同的特性和行为。主 Goroutine 只是在程序函数开始时自动创建的一个特殊 Goroutine,用于启动整个程序的执行。如果希望运行其他 Go 协程,Go 主协程必须继续运行着。如果 Go 主协程终止,则程序终止,于是其他 Go 协程也不会继续运行。

当我们提到主协程时,通常是指程序的入口协程,但在更复杂的并发程序中,可能会有多个并发执行的协程,它们在某种程度上都可以被称为主协程。这个称呼是相对而言的,取决于上下文和具体的程序结构

对于协程中的defer,C++语言里的析构函数(Destructor)可以起类似的作用,C++语言机制担保在对象被销毁前一定会执行析构函数中的代码。C++中的析构函数析构的是对象,Go中的defer析构的是函数。

1.2 CSP(Communicating Sequential Processes)

提倡通过通信共享内存而不是通过共享内存而实现通信

屏幕截图 2023-08-04 203300.png

1.3 Channel(通道,信道)

make(chan int) 无缓冲通道(同步通道)

make(chan int,2) 有缓冲通道(第二个参数是缓冲大小)

(make()函数专门用于创建切片、映射和通道)

屏幕截图 2023-08-04 204019.png 无缓冲的通道是指在发送数据和接收数据时,发送方和接收方必须同时准备好。也就是说,当发送方发送数据时,如果没有接收方正在等待接收数据,发送方将会被阻塞,直到有接收方准备好接收数据。同样地,当接收方准备好接收数据时,如果没有发送方正在发送数据,接收方也会被阻塞,直到有发送方准备好发送数据。

有缓冲的通道则允许在发送数据和接收数据时存在一定的缓冲空间。发送方可以一直发送数据,直到通道的缓冲区满了为止。而接收方可以一直接收数据,直到通道的缓冲区为空为止。当缓冲区已满时,发送方将会被阻塞,直到有接收方从通道中取出数据,腾出缓冲空间。同样地,当缓冲区为空时,接收方将会被阻塞,直到有发送方往通道中发送数据。

因此,无缓冲的通道保证了发送和接收的同步,而有缓冲的通道可以提供一定的异步性能。

通道发送操作:在表达式的左侧,<- 用于将一个值发送到通道中。例如,channel <- value 表示将 value 发送到名为 channel 的通道中。

通道接收操作:在表达式的右侧,<- 用于从通道中接收一个值。例如,value := <-channel 表示从名为 channel 的通道中接收一个值,并将其赋值给 value。

1.4 用time.Sleep(time.Second)

具体来说,time.Sleep()函数会暂停当前协程的执行,让其进入休眠状态,并阻塞该协程的执行一段时间。它接受一个time.Duration类型的参数,表示休眠的时间长度。

在time.Sleep(time.Second)中,time.Second表示一秒钟的时间长度。因此,该函数调用会使当前协程休眠一秒钟。

time.Sleep()函数常用于以下场景:

1.在并发程序中,通过休眠一段时间来模拟并发执行的协程之间的时间间隔。

2.在需要控制程序执行速度的情况下,可以使用time.Sleep()来降低程序的执行速度。

1.5 WaitGroup

使用WaitGroup需要以下步骤:

1.创建一个WaitGroup对象:var wg sync.WaitGroup(Sync 是 "synchronization" 的缩写)

2.在每个需要等待的协程开始执行前,调用wg.Add(1)来增加WaitGroup的计数器。(n个协程就wg.Add(n))

3.在每个协程的任务完成后,调用wg.Done()来减少WaitGroup的计数器。

4.在主协程(或其他需要等待的协程)中,调用wg.Wait()来等待所有协程完成任务。

屏幕截图 2023-08-04 213651.png

使用 defer wg.Done():在协程的任务代码中使用defer wg.Done()语句,会确保在任务执行结束时自动调用wg.Done()。这样可以避免遗漏调用wg.Done()的情况,即使在任务中发生了异常或提前返回,也能保证计数器被正确减少。

直接使用 wg.Done():在协程的任务代码中直接调用wg.Done(),需要手动确保在任务的适当位置调用wg.Done()。这样需要开发者自己保证在任务执行结束时调用wg.Done(),否则计数器不会被正确减少,会导致wg.Wait()一直阻塞。

使用defer wg.Done()可以确保在任务结束时自动调用wg.Done(),而直接调用wg.Done()需要手动保证在适当的位置调用。

二.依赖管理

2.1 Go依赖管理演进

GOPATH->GO Vendor->GO Module

2.2 依赖管理三要素

1.配置文件,描述依赖 go.mod

2.中心仓库管理依赖库 proxy

3.本地工具 go get/mod

2.3 依赖配置

2.3.1 依赖配置-go.mod

屏幕截图 2023-08-04 230121.png

2.3.2 依赖配置-version

屏幕截图 2023-08-04 230451.png

2.3.3 依赖配置-indirect

A->B->C

A->B 直接依赖

A->C 间接依赖

2.3.4 依赖配置-incompatible,依赖图

屏幕截图 2023-08-04 230827.png 屏幕截图 2023-08-04 231227.png

2.3.5 依赖分发-回源

屏幕截图 2023-08-04 231533.png

2.3.5 依赖分发-Proxy

屏幕截图 2023-08-04 231613.png

2.3.6 依赖分发-变量GOPROXY

屏幕截图 2023-08-04 231807.png

2.3.7 工具-go get

屏幕截图 2023-08-04 231916.png

2.3.8 工具-go mod

屏幕截图 2023-08-04 234052.png