这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记。
并发编程
并发与并行的区别:并发是多线程程序在一个核的CPU上运行,并行是在多个核的CPU上运行。一个线程包括多个协程。在go中使用goroutine实现并发编程,只需要在函数方法前添加go即可并发执行,独立于主线程之外,提倡通过通信共享内存而不是共享内存而实现通信。
下个例子中可以通过加锁来避免并发冲突。
var (
x int64
lock sync.Mutex
)
func main() {
add()
}
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock()
x += 1
lock.Unlock()
}
}
func addWithOutLock(){
for i := 0; i < 2000; i++ {
x += 1
}
}
func add() {
x=0
for i := 0; i < 5; i++ {
go addWithOutLock()
}
time.Sleep(time.Second)
fmt.Println("不加锁的条件下,执行完之后的x:",x)
x=0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
fmt.Println("加锁的条件下,执行完之后的x:",x)
}
执行结果为:
可以看到不加锁的时候x明显漏了很多,说明并发量大的时候会产生线程冲突。造成数据遗漏。除了采用加锁的方式也可采用waitgroup。
依赖管理
1、go vender
解决多个项目需要依赖同一个package依赖的冲突问题,但无法控制依赖的版本,更新项目可能会出现依赖冲突。
2、go module
即常用的go mod init \ go get 等
版本选择上会选择最低的兼容版本。
单元测试
单元测试可以不启动整个项目,来单独测试某一个功能,或者整个项目出现bug之后,通过单元测试快速查找bug位置。
func TestHelloTom(t *testing.T) {
output := HelloTom()
expect := "Tom"
if output != expect {
t.Error(expect,output)
}
println("success")
}
和Java中的spring-boot-starter-test依赖类似
func HelloTom() string {
return "Tom"
}
测试分支相互独立,全面覆盖。可在执行test的时候指定一些参数,
-
-cover 可以查看覆盖率
-
-run regexp 只运行 regexp 匹配的函数,例如:-run Array 那么就执行包含有 Array 开头的函数,该参数支持通配符
*,和部分正则表达式,例如^、$ -
-v 显示测试的详细信息 另外还有mock测试和基准测试,要实现每一次测试时都产生和之前一样的结果,不依赖于外部条件,则可以使用mock,在优化代码分析代码的时候则可以使用基准测试(testing.B)