文章目录
Go学习-Day8
- 个人博客:CSDN博客
单元测试
-
testing框架会将xxx_test.go的文件引入,调用所有TestXxx的函数
-
在cal_test.go文件里面写这个
go
package main
import "testing"
func TestAdd(t *testing.T) {
a, b := 1, 2
if add(a, b) != 4 {
t.Fatalf("Wrong Answer!")
}
}
-
在cal.go文件里写这个
go
package main
func add(a int, b int) int {
return a + b
}
-
运行go test -v的命令,就能运行单测
-
可以得到结果
shell
=== RUN TestAdd
cal_test.go:8: Wrong Answer!
--- FAIL: TestAdd (0.00s)
- testing框架import这个test文件之后,会调用所有TestXxx的函数,注意大写!
Goroutine
进程和线程
- 进程是程序的在操作系统的一次执行过程
- 线程是比进程更小的单位,一个进程能创建销毁多个线程
- 一个程序至少有一个进程,一个进程至少有一个线程
并发和并行
- 多线程在单核上运行,就是并发
- 多线程在多核上运行,就是并行
Go协程和主线程
-
主线程类似进程
-
协程类似线程,是轻量级的线程
-
协程的特点
- 有独立的空间
- 共享程序的堆空间
- 调度由用户控制
- 协程是轻量级的线程
go
import (
"fmt"
"strconv"
"time"
)
func test() {
for i := 0; i < 5; i++ {
fmt.Println("test() calls! " + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
func main() {
go test()
for i := 0; i < 5; i++ {
fmt.Println("main() calls! " + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
-
输出
shell
main() calls! 0
test() calls! 0
test() calls! 1
main() calls! 1
main() calls! 2
test() calls! 2
test() calls! 3
main() calls! 3
main() calls! 4
test() calls! 4
- go关键字会另起一个协程,主线程执行到这里会开一个协程并行执行,如果主线程执行完毕退出,协程会被强制退出
MPG模式
-
M(Machine)是操作系统的主线程,也就是物理线程
-
P(Processor)协程执行的上下文
-
G(Gorountine)协程
-
Go语言的协程是轻量级的,是逻辑态的,可以起上万个协程;而C/java的多线程是内核态的,几千个就会耗光CPU
CPU相关
go
runtime.NumCPU()
//获取本地CPU数目
runtime.GOMAXPROCS(int)
//设置GO最大可用的CPU数目
//Go Max Processors
协程并行的资源竞争
-
多个协程同时访问一个资源会发生冲突,会发生并发问题
-
在java中我们有锁和原子类来保证并发安全
-
声明一个全局锁变量lock
go
lock sync.Mutex
//sync是同步的意思,Muti-excluded互斥锁?
go
lock.Lock()//在进行并发的读写操作的时候,先上个锁
...//在进行操作的时候,别的协程会排队等待
lock.Unlock()//解锁之后,才能给别的协程使用
-
主线程读的时候也需要加锁,因为底层不知道协程已经解锁了,会发生资源冲突
-
但是这样不同协程之间没办法通讯,不知道什么时候协成完成任务了,白白空转浪费时间,或者提前结束主线程,终止协程,管道可能能解决这些问题,明天再学