这是我参与「第五届青训营」伴学笔记创作活动的第 2 天,我记下学习笔记,如果有错误的地方,希望大家可以帮我指出来并改正。
一、课堂整体梳理
上半节课以go语言进阶为主,主要讲述了协程、通道、安全锁等方面的内容;下半节课主要以项目的测试开发为主,介绍了项目开发的流程以及一些测试的工具。最后结合具体的项目进行讲解。
二、详细知识点总结
go语言进阶
在这之前我只知道并行是两个或两个以上的进程在同一时刻发生,而并发是指两个或两个以上的进程在同一时间间隔运行。然而通过课堂的讲解,我对并发与并行更加理解。并发是在单核cpu上运行的,是个伪“同时执行多个任务”,而并行是在多个核的cpu上运行的,这才是真正的“同时执行多个任务”。并行的每个核心都是独立运行的,互不影响。下图是我自己的理解。
对于go语言可以说就是为并发而生的,go语言很好的利用了多核的优势。
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 次迭代。
三、实践练习例子
项目开发思路:
遇到的问题:
第一次接触到sync.Once,下面来说一说我对sync.Once的理解。
sync.Once与init函数类似。但init函数是在首次被加载的时候执行,且只执行一次,而sync.Onc是在需要的时候执行,且只执行一次。 如果不希望有的部分在一开始的时候就被执行,那么就可以用 sync.Once 。
四、总结
通过本次课程,我收获了很多。目前也在准备项目中,课程的项目流程让我有了思路,也让我意识到需求分析的重要性,同时根据项目解析讲解,我也学到了新的方法。而且对索引的使用也更加深刻。继续加油吧!