进程
并发编程
- 并发与并行
go语言能充分发挥多核cpu的优势以高效运行
- 协程与线程
- 线程:内核态,栈Kb级别
是比较昂贵的系统资源,且比较消耗资源,创建切换停止都属于很重的系统操作
- 协程:用户态,轻量级线程,栈Mb级别
创建和调度有go语言本身去完成,一个线程可以并发的跑多个协程
协程
-
使用
调用函数时在函数前加“go”关键字,即可为此函数创建一个协程来运行
func main() { for i := 0; i < 5; i++ { go output(i) } time.Sleep(time.Second) } func output(a int) { fmt.Printf(a) }>>12034由结果我们可以看出是多协程并行
-
协程间的通信
go语言提倡通过通信实现共享内存,而不是共享内存实现通信
优势:通道将协程进行连接,遵循先入先出,能保证收发数据的顺序
(go也可以通过共享内存实现通信)
通道(Channel)
一种引用类型,需要用make关键字创建
无缓冲通道会使两个协程同步化,所以无缓冲通道也被称为同步通道
有缓冲通道代表通道中能存放多少元素,满了就会阻塞通道直到被取出
例:
func main() {
chan1 := make(chan int)
chan2 := make(chan int, 3)
go func() { //第一个协程
defer close(chan1)
for i := 0; i < 10; i++ {
chan1 <- i //将数字传进无缓冲通道
}
}()
go func() { //第二个协程
defer close(chan2)
for i := range chan1 { //将数字从无缓冲通道中取出
chan2 <- i * i //将数字的平方传入有缓冲通道
}
}()
for i := range chan2 {fmt.Print(i, " ")}//将数字从有缓冲通道中取出并输出
}
>>0 1 4 9 16 25 36 49 64 81
可以看出,输出是按顺序的,也就是说是线程安全的
Sync
- WaitGroup
用WaitGroup来进行协程同步的优化
开启携程后用Add(),在每个协程完成后用Done()方法使计数器减1,通过Wait()进行阻塞
依赖管理
- 迭代过程
GOPATH
- 原理:用go get直接将最新版本的包下载到src目录中,项目直接依赖src里面导的包
- 弊端:本地两个项目依赖不同版本的同个包时就可能会寄
Go Vendor
-
原理:
- 在项目目录中增加Vendor文件,所有依赖包的副本存在其中
- 依赖先去找Vendor,找不到就去找GOPATH
-
优点:解决了GOPATH的弊端
-
弊端:若项目同时依赖了B和C包,且B和C分别依赖A包的两个版本,就寄了
Go Module (类似于java的Maven)
-
1.11版本引入,默认打开
-
优点:可以定义版本规则和管理项目依赖的关系
-
使用:通过go.mod文件管理依赖包版本,通过go get/go mod 指令工具管理依赖包
-
依赖管理:
- 用于描述依赖的配置文件:go.mod
- 中心仓库管理依赖库:Proxy
- 本地工具:go get与go mod
-
go.mod
-
依赖配置
- 伪版本:
版本 - 提交某次commit时间戳 - 哈希码前缀
每次提交commit都会生成一个伪版本号
- 依赖图
当1.3和1.4相互兼容时,自动选择能够满足本次构建的最低兼容版本
-
依赖分发
依赖如何下载、去哪里下载的问题
-
直接依赖软件托管平台(github
- 无法保证构建稳定性:增删改软件版本
- 无法保证依赖可用性:删除软件
- 增加第三方压力:代码托管平台会有负载问题
-
Proxy
加一层Proxy层解决直接依赖代码托管平台的问题(适配器模式)
-
GOPROXY
如上例子,查找路径,根据配置,在Proxy1中查找,失败则下放到Proxy2,再失败则直接在代码托管平台下载
-
工具
-
go get
-
go mod(常用)
-
-