golang并发编程和测试 | 青训营笔记

206 阅读3分钟

这是我参与「第五届青训营」伴学笔记创作活动的第3天。今天复习了go的并发编程和测试相关内容,并将一些值得注意的点记录了下来。

协程

  • 协程在用户态,线程在
  • 内核态,协程比线程更加轻量。
  • Golang 中的并发是函数相互独立运行的能力。Goroutines 是并发运行的函数。Golang 提供了 Goroutines 作为并发处理操作的一种方式。
  • 创建一个协程非常简单,就是在一个任务函数前面添加一个go关键字。

通道

  • Go提供了通道机制,用于在 goroutine 之间共享数据
  • 需要在声明通道时指定数据类型。在任何给定时间只有一个 goroutine 可以访问数据项,因此不会发生数据竞争。
  • 根据数据交换的行为,有两种类型的通道:无缓冲通道和缓冲通道。无缓冲通道用于执行 goroutine 之间的同步通信,而缓冲通道用于执行异步通信。无缓冲通道保证在发送和接收发生的瞬间执行两个 goroutine 之间的交换。
  • 通道语法:
    • 创建:
        Unbuffered := make(chan int)   // 整型无缓冲通道
        buffered := make(chan int, 10) // 整型有缓冲通道
      
    • 通道值的发送和接收
        goroutine := make(chan string,10)
        goroutine <- "China"
        
        data := <-goroutine
      
  • 通道的发送和接收特性
    • 对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的。
    • 发送操作和接收操作中对元素值的处理都是不可分割的。
    • 发送操作在完全完成之前会被阻塞。接收操作也是如此。

WaitGroup实现同步

  • 使用Add()来增加信号量,使用Done()来表示执行结束,释放信号量,使用Wait()来等待未执行结束的协程。

runtime包

  • runtime包中定义了一些协程管理相关的api。
  • runtime.Gosched():让出cpu时间片,重新等待安排任务。
  • runtime.Goexit():退出当前协程。
  • runtime.GOMAXPROCS:设置同时执行协程的数量。

Mutex互斥锁实现同步

  • 除了使用通道实现同步之外,还可以使用Mutex互斥锁的方式实现同步。
        var lock sync.WaitGroup
        lock.Lock() //加锁
        lock.Unlock() //解锁
    

select

  1. select是Go中的一个控制结构,类似于switch语句,用于处理异步IO操作。select会监听case语句中channel的读写操作,当case中channel读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。

    select中的case语句必须是一个channel操作

    select中的default子句总是可运行的。

  2. 如果有多个case都可以运行,select会随机公平地选出一个执行,其他不会执行。

  3. 如果没有可运行的case语句,且有default语句,那么就会执行default的动作。

  4. 如果没有可运行的case语句,且没有default语句,select将阻塞,直到某个case通信可以运行

测试

测试分类

  • 回归测试:回归主流场景
  • 集成测试:系统功能的自动化测试
  • 单元测试:开发者对单独开发模块的验证
  • 单元测试成本最高,但测试覆盖率也相对较高。单元测试可以保证质量,提升效率。

单元测试的规则

  • 所有测试文件以 _test.go 结尾
  • 函数签名:func TestXxx(t *testing.T)
  • 初始化逻辑放到 TestMain 中:
        func TestMain(m *testing.M) {
            //测试前:数据装载、配置初始化等前置工作
            code := m.Run() //跑包下的所有单测
            //测试后:释放资源等收尾工作
            os.Exit(code)
        }
    

单元测试覆盖率

  • 即测试中实际run的代码行数占总代码行数的比例。
  • 可以通过增加 --cover 选项来打印单元测试覆盖率信息。
  • 一般覆盖率:50%~60%,较高覆盖率80%+
  • 测试分支要相互独立,全面覆盖
  • 测试单元粒度要足够小,函数单一职责

单元测试依赖处理

  • 幂等:重复处理case结果要相同
  • 稳定: 单元测试相互隔离,独立运行
  • mock机制:
    • 为一个函数或者方法打桩,进行模拟,从而避免依赖外部环境
    • 开源包:monkey : github.com/bouk/monkey

基准测试

  • 优化代码,需要对当前代码进行分析
  • 内置的测试框架提供了基准测试的能力
  • 函数签名:func BenchmarkXxx(b *testing.B)