这是我参与「第五届青训营」伴学笔记创作活动的第 2 天
Go语言进阶
Goroutine
Go语言通过协程实现高性能高并发编程,开启一个协程的方法很简单,调用的函数前使用go关键字即可开启一个协程执行这个函数。
func test(i int) {
fmt.Println(i, ",hello")
}
func main() {
for i := 0; i < 5; i++ {
go test(i)
}
time.Sleep(time.Second)
}
上面的程序实现了并行打印5个"hello",主函数结尾睡眠一秒是确保协程执行完毕主函数再退出。
Channel
Go语言的协程通讯可以通过Channel实现,使用make关键字创建一个Channel对象。
Channel又分为无缓冲和有缓冲两种类型,无缓冲通道又被称为同步通道。
chan1 := make(chan int)
chan2 := make(chan int,3)
并发安全
多线程同时读写同一个变量时可能会出现无法预料的结果,可以加锁实现并发安全。
func addWithoutLock() {
for i := 0; i < 2000; i++ {
sum++
}
}
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock()
sum++
lock.Unlock()
}
}
func main() {
for i := 0; i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
fmt.Println("Without lock:", sum)
sum = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
fmt.Println("With lock:", sum)
}
运行可以发现不带锁的程序并不总是输出预期结果,带锁可以保证同一时刻只有个协程对变量进行访问。
WaitGroup
前面的程序总是需要一个睡眠保证主函数不会在协程之前结束,但睡眠会导致CPU闲置,造成资源浪费,我们可以使用WaitGroup实现对线程的等待。
func main() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
fmt.Println(j, ",hello")
}(i)
}
}
Go依赖管理
Go Vendor
Go Vendor解决了多个项目依赖同一package的问题,但当项目依赖与两个package而这两个package又依赖于同一个package的两个不同版本时,Go Vendor就不适应了。
GO Module
通过go.mod文件管理依赖包版本。
通过go get/go mod 指令工具管理依赖包。
测试
软件的错误可能使产品不可用,对企业造成巨大的经济损失。
在开发之后进行测试可以在很大程度上避免严重的事故。
单元测试
所有文件以_test.go结尾,创建一个函数TestXX对XX进行测试,初始化逻辑放到TestMain中,测试函数效果是否与预期一致。
Mock
使用monkey库进行Mock测试,可以对函数,或者方法进行测试。
基准测试
测试程序的运行性能和对CPU资源的消耗程度,是对程序的性能分析。
通过基准测试的结果,去优化程序的性能。
项目实战
需求
社区话题页面,展示话题和回帖列表,仅实现web服务,话题和回帖数据存储在文件。
ER图
通过Entity Relationship Diagram明确结构。
项目的分层结构
分为数据层、逻辑层和视图层。
组件工具
Gin框架:高性能go web框架
Go Mod 管理依赖
实现
实现service、controller和router
测试运行
用上面学到的方法对代码进行测试。