这是我参与[第五届青训营]伴学笔记创作活动的第2天
并发编程
下面是学习课程的时候产生的一些疑问,解决这些问题之后无论是并发编程还是go语言都会有一个更好的理解
协程和线程有什么区别?
- 协程是用户态,轻量级线程
- 线程是内核态,线程可以跑多个协程
在java中我们一般讲的是线程,而在go中我们讲协程,这也是go语言为什么性能更好的一个比较重要的原因
并发和并行有什么区别?
- 并行是多个请求同一时间访问不同的资源
- 并发是多个请求同时访问相同资源,但在某个时间点只有一个请求,在一段时间内,这几个请求都经历了从开始到结束的过程
如何开启协程?
通过go来开启协程
channel有几种类型?分别如何创建?
channel有两种类型
make ( chan int) //无缓冲通道
make ( chan int,3)//有缓冲通道
通过make来进行创建,下面这个例子来自字节跳动内部课帮助来理解协程和通道
打印的结果为
0
1
2
4
9
16
25
36
49
64
81
刚开始看到这个例子,我们产生两个问题,下面是我的思考
问题一:这个打印的顺序为什么还是一致的,不是异步执行吗?
注意go的位置,是在整个for循环以为,那么表示放入通道src的数字是能保证顺序的
同理,通道dest放入的元素是从src中取出来的,主协程从通道dest自然也能保证顺序,也就呈现了顺序的打印结果
问题二:A协程和B协程都有对src这个通道的操作,会发生资源争抢吗?
当然不会,这是因为A协程在往通道里面放,而B协程是往通道里面取,这不是在资源争抢
问题三:为什么要设置协程?
通过上面两个问题,我们发现了通道src在顺序放入元素,通道dest在从src中顺序取出元素
并且往src放和从src中取这两个是操作是异步的,极大提高效率
什么是并发安全问题?如何解决?
在高并发的情况下,大量请求同时访问相同资源,造成资源争抢,数据非常容易出错,这就带来了并发安全问题
通过加锁可以解决并发安全问题,这是因为加锁后一个请求完成后释放锁,下一个请求才能访问资源,避免了资源争抢
waitGroup有什么用?具体实现原理是什么?
可以实现当想要的协程完成之后,主协程才能继续往后走,否则就会阻塞
实现原理是计数器,一个协程开始计数器+1,当这个协程结束时,计数器-1,只有当计数器归0前,主协程会阻塞
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()
}
打印结果
4
0
1
2
3
当5个协程结束后,主协程才会继续走
依赖管理
GOPATH由哪几个部分组成?
bin:项目编译的二进制文件
pkg:项目编译的中间产物,加速编译
src:项目源码(项目代码直接依赖于src下的代码)
为什么是使用Go Module来进行依赖管理?
Go Path: Project A和Project B依赖于某一个package的不同版本
Go Vendor: Package A 和 Package B无法依赖于某一个package的不同版本
Go Module能够解决这两个问题
依赖管理的三要素是什么?
配置文件:go.mod
仓库:Proxy(类似于java的maven仓库)
命令工具:go get/mod
测试
为什么要做测试?
开发之后直接上线可能会遇到很多错误,造成极大损失
因此在开发之后,上线之前要做测试
测试的几种类型?
测试有三类
- 回归测试:在特定需求场景下来做测试(根据需求文档)
- 基准测试:根据接口文档来做测试
- 单元测试:开发时间对于某个函数或者某个模块做的测试
什么是单元测试的覆盖率?
单元测试是测试代码的好坏,对于测试也有好坏之分,那如何确定测试的质量?就是通过单元测试的覆盖率(测试到的代码数量)
为什么使用MOCK来做单元测试?
单侧依赖于文件,如果文件修改或被损坏,单元测试很可能会fail,因此使用打桩函数(MOCK),保证测试case的稳定性
基准测试是什么?
除了对代码的逻辑是否准确进行测试,更高的要求就是对代码的性能进行分析,因此要做基准测试
通过内置的测试框架可以完成基准测试