go语言进阶 | 青训营笔记

130 阅读3分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 2 天,我记下学习笔记,如果有错误的地方,希望大家可以帮我指出来并改正。

一、课堂整体梳理

上半节课以go语言进阶为主,主要讲述了协程、通道、安全锁等方面的内容;下半节课主要以项目的测试开发为主,介绍了项目开发的流程以及一些测试的工具。最后结合具体的项目进行讲解。

二、详细知识点总结

go语言进阶

在这之前我只知道并行是两个或两个以上的进程在同一时刻发生,而并发是指两个或两个以上的进程在同一时间间隔运行。然而通过课堂的讲解,我对并发与并行更加理解。并发是在单核cpu上运行的,是个伪“同时执行多个任务”,而并行是在多个核的cpu上运行的,这才是真正的“同时执行多个任务”。并行的每个核心都是独立运行的,互不影响。下图是我自己的理解。 对于go语言可以说就是为并发而生的,go语言很好的利用了多核的优势。 image.png

Goroutine协程

协程的调度由go语言自动分配调动,协程是轻量级线程,可以理解成线程包括协程。协程在用户态,线程在内核态。因此go语言更适合高并发场景。

语法格式:

go func(形参){
    内容
}(实参)

协程是没有返回值的,这时候就要通过channel来获得返回值。

go语言是支持通过通信来共享内存的(channel),但也保留着通过共享来进行通信的(并发安全锁)。

channel通道(引用类型)

语法格式:

make(类型,[缓冲大小(可选)])

有缓冲的通道是异步的,相当于快递柜子,总数就那么多,只有有人取走快递,剩下的快递才可以放进快递箱。而无缓冲的通道是同步的。

Lock

方法:

lock.Lock()//加锁,获取临界区资源
lock.Unlock()//解锁,释放临界区资源

原理就是通过对临界区的保护机制,来实现并发的安全。

可能很晦涩难懂,下面我们来看个例子理解一下: 通过运行,我们可以发现,不加锁的结果是不确定的,而加锁的才是正确答案。在项目实战当中,也是有概率遇到这种问题的,所以我们一定要解决好。

package concurrence

import (
   "sync"
   "time"
)

var (
   x    int64
   lock sync.Mutex
)

func addWithLock() {
   for i := 0; i < 2000; i++ {//对临界区的保护
      lock.Lock()//获取临界区资源
      x += 1
      lock.Unlock()//释放临界区资源
   }
}//不加锁输出未知结果
func addWithoutLock() {
   for i := 0; i < 2000; i++ {
      x += 1
   }
}

func Add() {
   x = 0
   for i := 0; i < 5; i++ {
      go addWithoutLock()
   }
   time.Sleep(time.Second)
   println("WithoutLock:", x)
   x = 0
   for i := 0; i < 5; i++ {
      go addWithLock()
   }
   time.Sleep(time.Second)
   println("WithLock:", x)
}

func ManyGoWait() {
   var wg sync.WaitGroup
   wg.Add(5)
   for i := 0; i < 5; i++ {
      go func(j int) {
         defer wg.Done()
         hello(j)
      }(i)
   }
   wg.Wait()
}

WaitGroup

设计到的三个方法:

Add(delta int)//计数器+delta
Done()//计数器-1
Wait()//计数器为0时,并发任务都已经完成

计数器原理:开启协程+1,执行结束-1,主协程阻塞知道计数器为0。其实很好理解。 通过这个WaitGroup可以为了防止主协程结束时,子协程也停止的问题。

依赖管理

三要素:

  • 1.配置文件,描述依赖——>go.mod
  • 2.中心仓库管理依赖库——>Proxy
  • 3.本地工具——>go get/mod

测试

单元测试——规则

  • 1.所有测试文件以_test.go结尾,方便将测试与业务分开,便于测试
  • 2.创建测试函数,函数格式如下:
func TestXxx(*testing.T){ }
  • 3.初始化逻辑放到TestMain中

单元测试——覆盖率

命令行输入go test 文件名1 文件名2 --cover来测试覆盖率

单元测试——Mock保证稳定性

单元测试——benchmark基准测试

涉及到的知识点方法:

b.ResetTimer() // 重置计时器,忽略耗时的部分
func(b *B) RunParallel(body func(*PB))//创建多个 goroutine 并在它们之间分配 b.N 次迭代。

三、实践练习例子

项目开发思路:

image.png

遇到的问题:

第一次接触到sync.Once,下面来说一说我对sync.Once的理解。

sync.Once与init函数类似。但init函数是在首次被加载的时候执行,且只执行一次,而sync.Onc是在需要的时候执行,且只执行一次。 如果不希望有的部分在一开始的时候就被执行,那么就可以用 sync.Once

四、总结

通过本次课程,我收获了很多。目前也在准备项目中,课程的项目流程让我有了思路,也让我意识到需求分析的重要性,同时根据项目解析讲解,我也学到了新的方法。而且对索引的使用也更加深刻。继续加油吧!