Go的依赖管理与工程实践|青训营笔记
这是我参与「第五届青训营」伴学笔记创作活动的第12天
在后面会依次倒叙回顾之前的学习课程,便于复习~
一、课程重点内容
- Go的并发编程
- 依赖管理
- 单元测试
下面是对课程重点内容的思考与总结,有问题或者错误,可以批评指正呐~
二、Go的并发编程
我们知道在go语言里面启动协程是使用
go指令,这样会使得程序运行另一个子程序,也就是实现了并发,这里线程与协程又是什么区别呢?
-
协程与线程、进程
首先从一张图片看出线程、协程、进程之间的关系:
- 协程(coroutine)是一种程序组件,是一种比线程更加轻量级的存在。正如一个进程可以有多个线程,一个线程可以有多个协程。
- 在任务调度上,协程是弱于线程的。但是在资源消耗上,协程则是极低的。一个线程的内存在 MB 级别,而协程只需要 KB 级别。而且线程的调度需要内核态与用户的频繁切入切出,资源消耗也不小。
-
通道Channel
- 定义:通道是用来传递数据的一个数据结构,可以用于两个goroutine之间,通过传递一个指定类型的值来同步运行和通讯。这有点类似于条件量。
- 操作符:
<-,用于指定通道的方向,实现发送or接收,如果未指定方向,则默认双向 - 使用技巧:通道在使用前必须先创建;
-
并发控制
-
Mutex互斥锁:通过争夺锁,来使得两个或多个协程之间互相等待,而不是顺次执行,这样会导致性能急剧下降,由于激烈的争夺锁。
-
WaitGroup:使用方法如下
func main() { var waitGroup sync.WaitGroup start := time.Now() waitGroup.Add(5) for i := 0; i < 5; i++ { go func() { defer waitGroup.Done() time.Sleep(time.Second) fmt.Println("done") }() } waitGroup.Wait() fmt.Println(time.Now().Sub(start).Seconds()) } /* done done done done done 1.000306089 */使用
Add(5)表示我们有 5个 子任务,然后起了 5个 协程去完成任务,主协程使用Wait()方法等待 子协程执行完毕,输出一共等待的时间。WaitGroup 一共有三个方法:
(wg *WaitGroup) Add(delta int) (wg *WaitGroup) Done() (wg *WaitGroup) Wait()Add方法用于设置 WaitGroup 的计数值,可以理解为子任务的数量Done方法用于将 WaitGroup 的计数值减一,可以理解为完成一个子任务Wait方法用于阻塞调用者,直到 WaitGroup 的计数值为0,即所有子任务都完成
-
三、依赖管理
-
GOPATH
它是一个环境变量,主要由三个部分组成:
- bin:项目编译的二进制文件
- pkg:项目编译的中间产物,加速编译
- src:项目源码,项目代码直接依赖src下的代码
go get下载最新版本的包到src目录下
存在的弊端:无法实现package的多版本控制
-
Go Vender
- 位置:项目目录下增加vender文件,所有依赖包副本形式放在
$ProjectRoot/vender - 依赖寻址方式:
vender -> GOPATH - 通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题。
- 存在的弊端:更新项目的时候可能导致编译错误的冲突;无法控制依赖的版本。
- 位置:项目目录下增加vender文件,所有依赖包副本形式放在
-
Go Module
-
通过mod文件进行依赖管理,在发生依赖冲突时,只需要执行
go mod tidy即可,下载包使用go get -u packagename
-
四、单元测试
在开发文件夹下面建立一个XXX_test.go的文件,用于测试开发功能,在内部将函数名命名为TestXXXgo会自动识别该函数。
package greetings
import (
"regexp"
"testing"
"github.com/stretchr/testify/assert"
)
// TestHelloName calls greetings.Hello with a name, checking
// for a valid return value.
func TestHelloName(t *testing.T) {
name := "Gladys"
want := regexp.MustCompile(`\b` + name + `\b`)
msg, err := Hello("Gladys")
if !want.MatchString(msg) || err != nil {
t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
}
}
// TestHelloEmpty calls greetings.Hello with an empty string,
// checking for an error.
func TestHelloEmpty(t *testing.T) {
msg, err := Hello("")
assert.NotEqual(t, msg, " ", "msg error")
assert.NotEqual(t, err, nil, "err failed")
}
通过点击run test就能实现测功能,这里的话强烈推荐搭配assert,这样的话大大提高测试开发速度。
五、课程总结
通过学习并发编程,让自己对于并发控制的概念更加深入,其次是依赖管理,现在的版本红利使得管理依赖非常简单,最后是单元测试,这是一个项目成功的关键,一个好的项目就是在不断地测试下走向成功的。