【字节青训营-后端专场】Go 语言上手 - 工程实践
并发编程
并发vs并行
并发:多线程在一个核的cpu运行
并行:多线程在多个核的cpu运行
Goroutine
func hello(i int){
.......
}
func HelloGoRoutine(){
for i:=0;i<5;i++{
go func (j int){
hello(j)
}(i)
}
time.Sleep(time.Second)//防止协程结束前 主进程结束
}
CSP 协程之间的通信
go提倡通过通信共享内存
Channel
make(chan int)//无缓冲通道
make(chan int,2)//有缓冲通道
无缓冲通道:同步通道,发送接收几乎同时
有缓冲通道:取走才能放
考虑子协程A,B A发送数字,B计算数字的平方,主协程输出
发现最终输出有序
func CalSquare(){
src:=make(chan int)
dest:=make(chan int,3)//考虑主协程中的“消费”操作更加复杂,为解决“生产”和“消费”速率不均衡带来的效率问题,使用缓冲
go func(){
defer close(src)//延迟的资源关闭
for i := 0; i < 10; i++ {
src <- i
}
}()
go func(){
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
println(i)
}
}
并发安全 Lock
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(x)//10000
x = 0
for i := 0;i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
println(x)//可能<10000
}
WaitGroup
Add(delta int)//计数器+delta
Done()//计数器-1
Wait()//阻塞直到计数器为0
func hello(i int){
.......
}
func HelloGoRoutine(){
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()
time.Sleep(time.Second)//防止协程结束前 主进程结束
}
依赖管理
GOPATH
环境变量 其下有
bin目录:项目编译的二进制文件
pkg目录:项目编译的中间产物,加速编译
src目录:项目源码
项目代码直接依赖src下的代码
go get下载最新版本的包到src目录下
无法实现package的多版本控制(package版本更新)
Go Vendor
增加vendor文件夹 先在vendor下寻找,再去GOPATH下寻找
依赖冲突导致编译错误
Go Module
通过go.mod文件管理依赖包版本
通过go get/go mod指令工具管理依赖包
go.mod:配置文件,描述依赖
Proxy:中心仓库管理依赖库
go get/go mod:本地工具
单元测试
规则
测试文件以_test.go结尾
func TestXxx( * testing.T)
初始化逻辑放到TestMain中
func HelloTom() string {
return "Jerry"
}
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectOutput := "Tom"
if output != expectOutput {
t.Error("Expected %s do not match actual %s",expectOutput,output)
}
}
覆盖率
反映被测试函数被测试的函数占比
较高 80%+