go语言入门:工程实践
1.go的优势:
1.go可以发挥多核优势,高效运行高并发
协程(goroutine) :用户级,轻量级线程,栈KB级别
线程:系统级,轻量级线程,栈MB级别
线程上可以跑多个协程,go语言可以一次跑上万个协程。
一个协程小demo
package main
//快速打印
import (
"fmt"
"time"
)
func hello(i int) {
fmt.Println("hello goroutine:", i)
}
func HelloGoroutine() {
for i := 0; i < 5; i++ {
//调用协程的格式
go func(i int) {
hello(i)
}(i)
}
//等待防止主协程退出
time.Sleep(time.Second)
}
func main() {
HelloGoroutine()
}
/*打印结果:
hello goroutine: 0
hello goroutine: 1
hello goroutine: 4
hello goroutine: 2
hello goroutine: 3
*/
每次都是不一样的乱序说明打印操作是并发的
Channel:
建立语法:
make(chan elemtype,[bufSize])
//注:方括号[]表示可选
无缓冲通道:make(chan int) 通信是完全同步的
有缓冲通道:make(chan int,2)
channel demo:
package main
import "time"
func printSquare() {
src := make(chan int)
dest := make(chan int, 3)
//用于生产数字
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
//用于接收并计算平方,再输入到另一个带有缓冲区的通道dest中
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
println(i)
}
}
func main() {
printSquare()
}
一个关键的知识点:弄清缓冲与缓存
详见:性能优化--面试官问缓冲与缓存有什么区别? - 知乎 (zhihu.com)
这里总结一下我的理解:
*缓冲(buffer) *在两个端进行数据交换时,如果A产出物品B再拿走,那么当B没有拿走物品的时候A也无法产出。设置缓冲区的目的就是类似于包饺子时候放饺子皮的盆。让制作饺子皮和拿取饺子皮的人都可以按自己原来的节奏持续放/拿取。
*缓存(cache) *是针对局部性设计的为了将可能用到的已经调用过的数据来放到更高速的空间上来加快效率。
Sync关键字来控制线程/协程安全
并发安全lock:
package main
import (
"sync"
"time"
)
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("Withoutlock", x)
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
println("Withlock", x)
}
func main() {
Add()
}
不上锁时协程可能抢占导致数据更改出问题(并发安全问题)
WaitGroup(信号量):
Demo:利用waitgroup来优化代码
原始代码
优化后
2.依赖管理:
Go依赖管理演进路线
GOPATH->GO Vender->GO Module
Gopath:
项目直接依赖于src里的源代码。存在的问题:在package更新迭代后可能不适配。
GoVender:
在每个项目下面都增加有vendor文件夹,每个项目依赖的副本都放到这个文件夹下,在进行项目依赖的时候先到govender文件夹下找,如果找不到的话再去gopath。
Gomodule:
1.配置文件,描述依赖:go.mod
2.中心仓库管理依赖:Proxy
3.本地工具: go get/mod
项目依赖:
代码溯源:
如果直接从github这种的代码仓库去找不同版本的依赖的话可能存在着各种各样的问题,如果源代码进行修改,删除的话就寄了。而为了解决这个问题我们用来中间件proxy。
依赖的分发:
工具:
go get:
go mod: