后端进阶-工程进阶 | 青训营笔记

29 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天 why go比较快

image.png 并发是指在一个核上运行多个线程 通过时间片轮换的方式(此外也有广义高并发)

并行可以说是实现并发的一种手段

go通过高效调度高效率利用计算资源,发挥多核计算机的优势

image.png 图中应该是协程是kb级别

go语言可以一次创建上万个协程

image.png

开多个协程去打印。只需要在函数调用之前加上一个go就可以为函数调用创建一个协程来运行

最后sleep是保证子协程跑完之前主线程不会退出

因为是并行打印所以可能是乱序的

image.png

通道相当于把协程做了一个连接,相当于一个队列能保证顺序。通道就是可以让一个goroutine发送特定值到另一个goroutine的机制

go仍然保留了通过共享内存实现通信(对临界区加锁,修改临界区,这样一定程度上会影响程序的性能,所以go提倡了通过通信来共享内存)

无缓冲通道又叫同步通道

有缓冲通道上的容量就是可以存放元素的大小,类比于货架,也可以认为是一个生产者消费者模型

image.png

src实现ab协程之间的通信

dest实现bm协程之前的通信

注意到一般是发起通信者去关闭通道

图中的实现是能够保证顺序性的,是并发安全的。使用带缓冲的队列是因为生产者的逻辑比较简单,会比消费者快。这样就不会因为消费者的速度慢影响生产者的执行效率

image.png

通过共享内存实现通信,存在多个goroutine同时操作一块内存资源的情况

如果没有加锁,最后数字不是10000,即有并发问题。

注意上面的代码打印x的时候要先sleep一下

image.png

使用waitgroup实现并发任务的同步。每完成一个并发任务让计数器-1

image.png

当计数器为0时,wait才会解除阻塞

image.png

image.png

目前为止go module是普遍使用的

因为一个依赖在src里只有一个版本

归根结底 go vender也是在依赖源码,并不能很好的标识版本的概念

这个相当于在一个项目中指定了某个依赖的唯一版本。但是可能出现不同的依赖又依赖于同一个依赖的不同版本。这就出现了问题


module path就是对应的第一行那个module… 后面跟上版本号就能唯一确定指定包的指定版本或者某一个commit

每次commit go都会帮我们生成一个伪版本号

上图的第二条规则就是因为一些项目比较早,当时还有go mod 因此命名无法遵循第一条规则。为了引入这些早期项目,添加了一个+incompatible   这个词的意思是不兼容的。相当于做一个标识表明可能会出现一些不兼容的代码逻辑

go会选择满足本次构建的最低版本,1.3 1.4本身处于同一个大版本下,理应兼容。

如果c此时已经更新到1.5版本,go也不会去选1.5而是选1.4。这就是选择最低的兼容版本

依赖可以从github这样的代码托管平台下。但有缺陷(如图),比如github的作者把某个软件仓库删了,就可能造成之前写好的代码找不到依赖

go proxy会对依赖进行稳定的缓存。保证依赖的稳定性,实现稳定可靠的依赖分发。类似于java中maven的中央仓库。相当于一种适配器的思想,上层解决不了下层的需求,就多加一层

go通过环境变量来控制go proxy

命名正确就会有一个可以运行的标志

使用开源的assert包来进行判断

加上--cover参数就能在测试的同时打印覆盖率

幂等:重复运行一个case的时候与之前的结果应该是要一样的

稳定性:单元测试是相互隔离的,每个测试都可以独立运行

为了避免测试对真正的数据源产生影响,可以使用mock

打桩就是以一个打桩函数去替换原函数

targe就是要被替换的函数,

unpatch就是为了保证在测试结束后把桩卸载掉

底层实现就是在运行时通过go的unsafe包将函数地址替换成打桩函数地址。最终运行了打桩函数

最终实现了mock

函数地址替换,相当于以后执行ReadFirstLine就会执行后面那个函数,不再执行原来的函数