Go语言入门-工程实践

90 阅读3分钟

GO的一个优势特点

  • go可以充分发挥多核优势,高效运行

语言进阶

1.1Goroutine

  • 协程和线程的定义:

    协程:用户态,轻量级线程,栈KB级别

    线程:内核态,线程跑多个协程,栈MB级别

    协程和线程的区别在于:一个线程可以多个协程,一个进程也可以单独拥有多个协程。线程进程都是同步机制,而协程则是异步。协程避免了无意义的调度,由此可以提高性能

    eg:

func hello(i int){
    println("hello goroutine:"+fmt.Sprint(i))
}

func HelloGoRoutine(){
    for i :=0;i<5;i++{
        go func (j int){
            hello(j)
        }(i)
    }
    time.sleep(time.Second)
}

解析:

  • 它创建了5个goroutine并发运行hello函数。每个goroutine都会打印一条消息。最后,主goroutine会睡眠1秒钟以等待其他goroutine完成。

1.2CSP

CSP(Communicating Sequential Processes)是一种并发编程模型,用于描述两个独立的并发实体通过共享的通讯 channel(管道)进行通信。

  • 注:推荐通过通信共享内存而不是通过共享内存而实现通信

1.3Channel

Go 语言中的通道具有以下几个特性:线程安全、阻塞式发送和接收、顺序性、可以关闭、缓冲区大小。

我们可以使用make函数创建一个通道,并通过<-操作符进行数据的发送和接收。通道可以具有不同的类型,用于限制发送和接收的数据类型。

缓冲用法:make(chan 元素类型,[缓冲大小])

  • 无缓冲通道 make(chan int)
  • 有缓冲通道 make(chan int,2)

eg:

func CalSquare(){
    src:=make(chan int)
    dest:=make(chan int,3)
    go func(){
     defer close(src)
     for i:=0;i<10;i++{
         src<-i
     }
   }()
   go func(){
       defer close(dest)
       for i :=range src{
           dest<-i*i
       }
   }()
   for i:=range dest{
       println(i)
   }
}

解析:

  • 第一个子协程发送0-9数字
  • 第二个子协程计算输入数字的平方
  • 最后由柱协程输出最后的平方数

通道是一种特殊的数据结构,可以在不同的协程之间传递值。它提供了一种通过发送和接收操作进行同步的方式。通过使用通道,协程可以安全地发送数据到通道,其他协程可以从通道中接收到这些数据并进行处理。

1.4并发安全LOCK

go语言中有很方便的用于解决并发的安全性问题

lock.Lock()

lock.Unlock()

在 Lock 和 Unlock 之间的代码段称为资源的临界区(critical section),是线程安全的,任何一个时间点都只能有一个协程执行这段区间的代码。

测试

1.常见事故

  1. 营销配置错误
  2. 用户提现,幂等失效
  3. 代码逻辑错误,广告位被占
  4. 代码指针使用错误,导致app不可用

2.常用测试逻辑

回归测试->集成测试->单元测试

从左到右覆盖率逐层变大,成本逐层降低

3.1单元测试

  • 单元测试规则:
  1. 所有测试文件以_test.go结尾
  2. func TestXxx(*tesing.T)
  3. 初始化逻辑放到TestMain中

3.1.1单元测试-Tips

  • 一般覆盖率:50%-60%,较高覆盖率80%+
  • 测试分支相互独立、全面覆盖
  • 测试单元粒度足够小,函数单一职责

3.2单元测试依赖

在编写单元测试时,有时会遇到被测代码依赖于外部资源或其他组件的情况。这些依赖可能会影响测试的可靠性和可控性。

为了解决这个问题,可以使用 Mock 技术来模拟被测代码的依赖关系。Mock(见下文3.4) 可以帮助我们在测试中隔离被测代码,使测试更加可靠和可控。

3.3试文件处理

在测试中,有时需要处理文件,例如读取测试数据或写入测试结果。Go 语言的标准库提供了丰富的文件操作函数,可以帮助我们在测试中处理文件。

  • 例如,可以使用 ioutil 包中的 ReadFile 函数来读取文件内容:
data, err := ioutil.ReadFile("testdata.txt")
if err != nil {
    // 处理错误
}
// 使用 data
  • 也可以使用 os 包中的 Create 和 Write 函数来创建并写入文件:
file, err := os.Create("testdata.txt")
if err != nil {
    // 处理错误
}
defer file.Close()
_, err = file.Write([]byte("test data"))
if err != nil {
    // 处理错误
}

3.4Mock

Mock 是一种测试技术,用于模拟代码中的依赖关系。它可以帮助我们在测试中隔离被测代码,使测试更加可靠和可控。

在 Go 语言中,可以使用第三方库来实现 Mock。例如,可以使用 gomock 库来快速创建 Mock 对象。

要为一个函数或方法打桩,可以使用 gomock 库提供的 EXPECT() 方法来定义期望的行为。例如:

ctrl := gomock.NewController(t)
defer ctrl.Finish()
m := NewMockMyInterface(ctrl)
m.EXPECT().MyMethod(gomock.Any()).Return(1)

快速Mock函数

  • 为一个函数打桩
  • 为一个方法打桩

3.5基准测试

基准测试(Benchmark)是一种测试方法,用于测量代码的性能。它可以帮助我们了解代码的运行效率,并为优化代码提供参考。

  • 优化代码,需要对当前代码分析
  • 内置的测试框架提供了基准测试的能力

基准测试过程:

  1. 运行
  2. 优化

Go 语言内置的测试框架提供了基准测试的能力。要编写基准测试,只需创建一个以 Benchmark 开头的函数,并接受一个 *testing.B 类型的参数。在函数体中,使用 b.N 来控制测试循环的次数。

func BenchmarkMyFunction(b *testing.B) {
    for i := 0; i < b.N; i++ {
        MyFunction()
    }
}