携程
协程与线程的区别:
协程:运行在用户态,而且相比较于线程开销更小。
线程:线程属于内核态资源,由多个线程组成。
通过go来开启一个协程
package main
import (
"fmt"
"time"
)
func hello(j int) {
fmt.Println("hello:", j)
}
func main() {
for i := 0; i <= 4; i++ {
go hello(i)
}
time.Sleep(time.Second)
}
协程通信
协程通信可以有两种方式:
-
通过通信来实现共享内存(主要)
-
通过共享内存来实现通信。
创建channel
使用make(chan 元素类型,缓冲区大小) 来创建,没有缓冲区大小字段则为无缓冲(同步)通道
比如make (chan int, 2)来创建一个缓冲区大小为2的管道,make(chan int)来创建一个无缓冲通道。
示例代码:
package main
import (
"fmt"
)
func hello(j int) {
fmt.Println("hello:", j)
}
func main() {
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 {
fmt.Println(i)
}
}
锁机制:
package main
import (
"fmt"
"sync"
"time"
)
var x int64
var lock = sync.Mutex{} //获取一把锁
func addWithoutLock() {
for i := 1; i <= 2000; i++ {
x++
}
}
func addWithLock() {
for i := 1; i <= 2000; i++ {
lock.Lock()
x++
lock.Unlock()
}
}
func main() {
x = 0
for i := 1; i <= 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
println(x)
x = 0
for i := 1; i <= 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
fmt.Println(x)
}
WaitGroup
开启一个协程+1,结束一个协程-1,主协程阻塞直到计数器为0。
package main
import (
"fmt"
"sync"
)
func hello(j int) {
fmt.Println("hello:", j)
}
func main() {
var wg = sync.WaitGroup{}
wg.Add(5) //有五个协程所以直接+5
for i := 0; i <= 4; i++ {
go func(j int) {
defer wg.Done()//标记协程已经完成
hello(j)
}(i)
}
wg.Wait() //等待计数器为0
}
依赖管理
管理方式
GOPATH
主要使用文件结构
- pkg
- bin
- src
缺点:没有多版本依赖控制
Go Vendor
在Vendor中保存一个项目依赖的副本
缺点:
- 无法控制依赖版本
- 可能存在依赖冲突,导致编译出错的问题
Go mod(目前)
通过go.mod文件管理以来包版本
通过go get / go mod置顶工具管理依赖
管理三要素
1.配置文件,描述依赖
2.中央仓库代理
3.本地工具
依赖配置Version
语义化版本:${MAJOR}.${MINOR}.${PATCH}如V1.3.0,V2.3.0
基于Commit的伪版本vx.0.0-yyyymmddhhmmss-abcdefg1234
测试
单元测试
测试方法:通过编写xxx_test.go文件(标准)来进行测试
评估标准:代码覆盖率(具体执行了多少行的代码)
一般覆盖率:50~60%
较高覆盖率:80%以上
Mock测试
测试文件存在本地依赖的情况(如文件等),此时可以使用Mock测试来替换相对应的函数,摆脱本地环境的依赖。
基准测试
- 优化代码
- 测试代码运行速度