Go语言进阶 | 青训营笔记

77 阅读3分钟
这是我参与「第五届青训营」伴学笔记创作活动的第 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

测试运行

用上面学到的方法对代码进行测试。