##这是我参与[第五届青训营]笔记创作的第二天
1、语言进阶
1、并发 vs 并行
并发:多线程程序在一个核的cpu上运行
并发:多线程程序在多个核的cpu上运行
Go可以充分发挥多核优势,高效运行
2、协程
协程:用户态,轻量级线程,栈kb级别
例句:快速打印:
package main
import (
"fmt"
"time"
)
func hello(i int) {
println("hello goroutine : " + fmt.Sprint(i))
}
func main() {
for i := 0; i < 5; i++ {
go func(j int) { //通过go来开启协程
hello(j)
}(i)
}
time.Sleep(time.Second)
}
1、协程之间的通信
通过通信共享内存
提倡通过通信共享内存而不是通过共享内存而实现通信
2、Channel
make(chan元素类型,【缓冲大小】)
- 无缓冲通道 make(chan int)
- 有缓冲通道 make(chan int,2)
package main
import "fmt"
func main() {
src := make(chan int)
dest := make(chan int, 3)
go func() {
defer close(src) //推迟关闭src
for i := 0; i < 10; i++ {
src <- i //将i发送到src
}
}()
go func() {
defer close(dest) //推迟关闭dest
for i := range src { //遍历src赋值给i
dest <- i * i //将i*i 发送到dest
}
}()
for i := range dest {
fmt.Println(i)
}
}
3、并发安全 Lock
这里运用到了一个sync包,使用Mutex类型锁实现
Mutex 是最简单的一种锁类型,同时也比较暴力,当一个 goroutine 获得了 Mutex 后,其他 goroutine 就只能乖乖等到这个 goroutine 释放该 Mutex。
package main
import (
"sync"
"time"
)
var (
x int64
lock sync.Mutex
)
以下写两种,一种写了锁,一种不写锁
写了锁,通过locl.lock()获得锁
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock() //获得锁
x = x + 1
lock.Unlock()
}
}
不写锁
func addWithOutLock() {
for i := 0; i < 2000; i++ {
x = x + 1
}
}
开启锁协程
func main() {
x = 0
for i := 0; i < 5; i++ {
go addWithOutLock() // 开启协程
}
}
开启没锁协程
for i := 0; i < 5; i++ {
go addWithLock()
}
同时运用time.Sleep(time.second)阻塞一个协程的执行直到d时间结束。
time.Sleep(time.Second)
最后组合起来:
package main
import (
"sync"
"time"
)
var (
x int64
lock sync.Mutex
)
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock() //获得锁
x = x + 1
lock.Unlock()
}
}
func addWithOutLock() {
for i := 0; i < 2000; i++ {
x = x + 1
}
}
func main() {
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)
}
4、WaitGroup
属于Syns包
实现并发任务的同步
拥有三个方法:
- Add() 计数器+delta
- Done() 计数器-1
- Wait() 阻塞直到计数器0
计数器: 开启协程+1;执行结束-1;主协程阻塞直到计数器为0。
可以通过以下time.Sleep例子来理解WaitGroup
func hello(i int) {
println("hello" + fmt.Sprint(i))
}
func main() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second) // 运用time.Sleep(time.second)阻塞
}
现在使用WaitGroup
defer 语句用于延迟函数的调用,每次 defer 都会把一个函数压入栈中,函数返回前再把延迟的函数取出并执行。 Golang 中的 defer 可以帮助我们处理容易忽略的问题,如资源释放、连接关闭等
func hello(i int) {
fmt.Println("hello goroutine :" + fmt.Sprint(i))
}
func main() {
var wg sync.WaitGroup // 声明一个等待组
wg.Add(5) //计数器+5
for i := 0; i < 5; i++ {
go func(i int) {
defer wg.Done()//计数器减-1
hello(i)
}(i) //调用for循环的i实参
}
wg.Wait() // 阻塞,直到计数器为零
}
2、依赖管理
1、背景
- 工程项目不可能基于标准库0~1编码搭建
- 管理依赖库
2、管理演进
- GOPATH
- GO Vendor
- Go Module
1、GOPATH
环境变量:
- bin: 项目编译的二进制文件
- pkg:项目编译的中间产物。加速编译
- src:项目源码
项目代码直接以来src下的代码
go get 下载最新版本的包到src目录下
2、Go Vendor
项目目录下增加vendor文件
3、Go Module
通过go.mod文件管理以来包版本
通过go get/go mod指令工具管理依赖包
:定义版本规则和管理项目的依赖关系
依赖管理三要素
- 配置文件,描述依赖 go.mod
- 中心仓库管理依赖库 Proxy
- 本地工具 go get/mod
依赖配置-go.mod
依赖配置-version
语义化版本
${MAJOR} ${MINOR} ${PATCH}
V1.3.0
V2.3.0
基于commit 伪版本号
依赖配置-indirect
A->B->C
- A->B直接依赖
- A->C间接依赖
对于没有直接依赖的依赖模块会用indirect标识
依赖配置-incompatible
- 主版本2+模块会在模块路径增加/vN后缀
- 对于没有go.mod文件并且主版本2+的依赖,会+incompatible