这是我参与「第五届青训营 」伴学笔记创作活动的第2天 可能我没什么基础吧,昨天的课没咋听懂,今天的课就听的一头雾水,只能记录点自己勉强能听懂的东西了,go语言学习的路程真是艰难啊
Go语言为什么这么快? 因为Go可以充分发挥多核优势,高效运行
协程Goroutine 协程:用户态,轻量级线程,栈KB级别。 线程:内核态,线程跑多个协程,栈MB级别。 这使得Go语言一次可以创建上万级别的协程。 通过go关键字来开启goroutine,是一个轻量级线程,其调度是由Golang运行时进行管理的 代码案例: package main
import ( "fmt" "time" )
func HelloPrint(i int) { // println("Hello goroutine : " + fmt.Sprint(i)) fmt.Println("Hello goroutine :", i) }
// 效果就是快速且无序打印
func HelloGoroutine() { for i := 0; i < 5; i++ { go func(j int) { HelloPrint(j) }(i) } // time.Sleep()的作用是:保证了子协程在执行完之前,主协程不退出。 time.Sleep(time.Second) }
func main() { HelloGoroutine() }
有通过通信共享内存和通过共享内存实现通信两种方法 提倡通过通信共享内存而不是通过共享内存而实现通信
通道Channel make(chan 元素类型,[缓冲大小]) 无缓冲通道 make(chan int) 有缓冲通道 make(chan int,2)
无缓冲通道也被称为同步通道; 有缓冲通道可以类比红石中继器(但是有点不一样,只是效果类似);
通道是用来传递数据的一个数据结构,可以用于两个goroutine之间,通过传递一个指定类型的值来同步运行和通讯。 操作符<-用于指定通道的方向,实现发送or接收; 特别地,若未指定方向,则为双向通道; 通道在使用前必须先创建;
ch <- v // 把 v 发送到通道 ch v := <-ch // 从 ch 接收数据 并把值赋给 v
package main
import ( "fmt" )
func CalcPow() { src := make(chan int) dest := make(chan int, 3) // 子协程src发送0~9数字 go func() { defer close(src) // 当子协程src结束的时候再关闭,减少资源浪费 for i := 0; i < 10; i++ { src <- i } }() // 子协程dest计算输入数字的平方 go func() { defer close(dest) // 通过 range 关键字来实现遍历读取到的数据 for i := range src { dest <- (i * i) } }() // 主协程输出最后的答案 // 这里可以暂时认为子协程需要使用匿名函数 for i := range dest { // 因为主协程可能会有更多的复杂操作,比较耗时,所以用带缓冲的通道可以避免问题 fmt.Println(i) } }
func main() { CalcPow() }
并发安全Lock 当采用共享内存实现通信(上图的第二种)的时候,会发生一些Undefine问题,这时候就需要并发安全。 Mutex互斥锁来实现同步 同步的含义:两个或多个协程之间互相等待,而不是顺次执行。
package main
import ( "fmt" "sync" "time" )
// 除了使用channel实现同步之外,还可以使用Mutex互斥锁来实现同步。
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) fmt.Println("WithoutLock :", x)
x = 0
for i := 0; i < 5; i++ {
go AddWithLock()
}
time.Sleep(time.Second)
fmt.Println("WithLock :", x)
}
func main() { Add() } /* Output: WithoutLock : 8014 WithLock : 10000 */
单元测试: 回归测试,集成测试,单元测试 从左到右,覆盖率逐层变大,成本却逐层降低
单元测试规则
所有测试文件以_test.go结尾
func TestXxx(*testing.T)
初始化逻辑放到TestMain中
代码覆盖率:
衡量代码是否经过了足够的测试
评价项目的测试水准
评估项目是否达到了高水准测试等级
func JudgePassLine(score int16) bool {
if score >= 60 {
return true
}
else {
return false
}
}
func TestJudgePassLineTrue(t *testing.T) {
isPass := JudgeePassLine(70)
assert.Equal(t, true, isPass)
}
func TestJudgePassLineFalse(t *testing.T) {
isPass := JudgeePassLine(50)
assert.Equal(t, false, isPass)
}
一般覆盖率在50%~60%,较高覆盖率在80%+
测试分支相互独立、全面覆盖
测试单元粒度足够小,函数单一职责
单元测试-依赖
幂等:重复运行同一个case,结果与之前一致
稳定:指单元测试相互隔离,可以独立运行
单元测试-文件处理
func ReadFirstLine() string {
open, err := os.Open("log") // 打开一个文件
defer open.Close()
if err != nil {
return ""
}
scanner := bufio.NewScanner(open) // 对每行进行遍历
for scanner.Scan() {
return scanner.Text()
}
return ""
}
func ProcessFirstLine() string {
line := ReadFirstLine()
destLine := strings.ReplaceAll(line, "11", "00") // 替换11为00
return destLine
}
func TestProcessFirstLine(t *testing.T) {
firstLine := ProcessFirstLine()
assert.Equal(t, "line00", firstLine)
}