Go第二天上课

115 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天.

视频首先介绍了三个基础概念,协程高并发,通道概念以及安全锁的概念

go支持比线程更轻量级的协程,语法为go func(),此时主线程会开辟协程,协程独立于主进程的运行,所以会存在主进程运行完而协程还未结束的情况,每次调用go都会开辟一个新协程,所以运行更快。 在go语言中协程会互相通信,通信的介质就是内存。

协程之间进行通信时可能会操作同一块内存空间,所以该空间的值可能会被多个协程同时读取,所以go提倡通过通信来共享内存,而不是共享内存来实现通信。若需要多个协程来读取临界区的数据,而临界区的数据可被修改时,就会存在安全问题,可能会造成两个及多个协程读取到同一数值同时可能会对该数值进行更改。所以需要安全锁机制来保证协程一个一个访问该内存资源。

锁机制需要引入sync包

lock sync.Mutex//保护临界资源,防止多个go协程同时访问一个内存,锁机制有效的保证一次只能有一个go进行访问 

func addWithLock()  {
   for i:=0;i<200;i++{
      lock.Lock()
      x += 1
      lock.Unlock()
   }
}  
go addWithLock()

锁机制在该代码中能保证x的值不会同时被同时读取,所以x值受到锁机制的保护

随后视频介绍了协程资源释放的例子,需要引入等待同步组sync.WaitGroup,开启多个go协程,当主进程结束后可能协程还未结束,若想保证协程都运行结束后主进程再结束,就需要WaitGroup机制,类似于计数器,当程序开启一个协程后wg.Add(1),等待协程结束后进行释放defer wg.Done()。随后进行等待wg.Wait(),最后所有协程结束后主进程才会退出。

Go依赖问题 go管理依赖三要素,配置文件go.mod,中心仓库管理依赖proxy,本地工具go get/mod。 其中go.mod文件中包含代码所需要的非基本的库,需要从外部获取,利用本地工具进行拉取,依赖可能通过多层中心仓库进行获取,若通过多层中心仓库获取则是indirect。

image.png

代码测试问题

项目测试分为三种,回归测试,集成测试,单元测试。回归测试即在客户端运行查看可能存在的bug,单元测试则需要对代码内一个个函数模块进行测试,以防止最大限度的程序问题。 代码,测试规范如下,需要对测试的函数后缀加上_test,在测试函数中前面加上Test,以及参数加上t *testing.T。

image.png

在测试中,将需要测试的函数输出值与期望的输出值进行对比,可以查看对比结果,以此来检测代码的故障问题,其中引入assert函数,该函数可以直接调用方法Equal进行对比,同时在测试阶段可以调用 go test XXX.go XXX_test.go --cover的语法查看代码覆盖率,由于输入的值可能不会使整个代码跑完整,在含有if分支时剩余部分可能不会进行测试,所以代码覆盖率越高说明程序健壮性更强。

package Test

import  "testing"
import "github.com/stretchr/testify/assert"

func TestHelloTom(t *testing.T)  {
   output := HelloTom()
   expectOutput := "Tom"
/* if output != expectOutput{
      t.Errorf("expected %s do not match actual %s",expectOutput,output)
   }*/
   assert.Equal(t,expectOutput,output)

}

若测试的函数中包含本地文件的输入,由于本地文件可能会被更改,所以测试函数可将函数含风险部分进行替换。利用Mock进行替换,需要引入bou.ke/monkey包

package Test

import "strings"

//打桩测试,当对外部文件需要进行测试时,外部文件可能会对值进行替换
//所以打桩测试将一个测试函数替换原函数,最终使得原函数无论是否被恶意更改都不影响测试函数。利用MOCK打桩

func FileReplace() string {//利用函数代替外部读入,因为该函数会被替换所以不影响
   //println("hello school")
   return "line110111"
}

func ProcessFirstLine() string {
   line := FileReplace()
   destLine := strings.ReplaceAll(line,"11","00")
   return destLine
}
package Test

import (
   "github.com/stretchr/testify/assert"
   "testing"
   "bou.ke/monkey"
)

func TestProcessFirstLineMock(t *testing.T) {
   monkey.Patch(FileReplace, func()string {
      return "line110"
   })
   defer monkey.Unpatch(FileReplace)
   line:=ProcessFirstLine()
   assert.Equal(t, "line000",line)
}

该程序中利用 func()string { return "line110" }匿名函数替换FileReplace函数,所以返回结果是line110代替line110111进行输入,所以无论FileReplace函数的结果传回任意值都会被匿名函数所替换monkey.Patch(a,b)其中b作为a的代替函数,a为原函数,最后匹配不影响ProcessFirstLine()函数内的其他不确定函数的干扰,当文件函数被更改不影响ProcessFirstLine()函数的正确性。

最后老师讲了工程代码设计思路以及实现。对目前而言还有一定的难度,需要继续提升。 上课收获很大,老师讲的内容需要下来仔细练习并且反复观看,有时候第一遍可能不能理解的部分,当自己手敲过后也许能有更深的体会。