Go语言进阶学习 | 青训营

69 阅读3分钟

go并发

并发

go实现了两种并发形式,一种是多线程共享内存,另一种就是Go、语言推荐的CSP并发模型。
不要以共享内存的方式来通信,相反,要通过通信来共享内存。

1. Goroutine

线程->协程

相信大家都多多少少的了解到了线程,那么协程是什么存在呢?

  • 协程,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。
  • 协程不是被操作系统内核所管理,而完全是由程序所控制。(性能有所提升)
  • 协程能保留上一次调用的状态,每次重入时,就相当于进入上一次调用的状态。

程序示例

程序简单演示了使用go关键字创建多个协程。
这里有一点,如果在主函数使用go goroutine() fmt.Println("........"),那么程序只会输出........
当协程创建完毕之后,主函数立即返回继续执行下一行代码,不像函数调用,需要等函数执行完成。主协程执行完毕,程序便退出,goroutine 协程随即也退出,便不会有输出。
如果想要协程执行完成的话,可以加一个休眠时间

func goroutine() {  
for i := 0; i < 5; i++ {  
go func(j int) {  
hello(j)  
}(i)  
}   
}  

func hello(i int) {  
fmt.Println("hello goroutine : " + fmt.Sprint(i))  
}

执行结果:
hello goroutine : 4
hello goroutine : 1
hello goroutine : 3
hello goroutine : 0
hello goroutine : 2

2. CSP (communicating sequential processes)

CSP并发模型通过goroutinechannel来实现的

3. channel

使用make(chan 元素类型,[缓冲大小])可以生成一个channel,传输数据使用channel <- 数据, 接受数据使用 <- channel。

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)  
}

4. 并发安全Lock

锁机制,我们下面使用的Mutex是互斥锁的意思,也叫排他锁,同一时刻一段代码只能被一个线程运行,使用只需要关注方法Lock和Unlock。
在不加锁的情况下,多协程去执行代码出现的结果可能不一,(我执行了几次大概三千左右),而加上锁之后,结果都是5000。因为加上锁之后协程都会去执行++操作。

//mutex := sync.Mutex{}  
x := 0  
for i := 0; i < 5; i++ {  
go func() {  
for j := 0; j < 1000; j++ {  
//mutex.Lock()  
x++  
//mutex.Unlock()  
}  
}()  
}  
fmt.Println(x)

5. WaitGroup

waitgroup,等待其他协程执行完一些代码,等待获取结果。是一种常见的并发控制方式,有点类似于信号量,常用的有三个方法。

  • ADD(delta int) // 初始化计数器的值
  • Done() //计数器-1
  • Wait()// 阻塞,直到计数器的值为0后才会解除状态

依赖管理

1. go依赖演进

GOPATH =>Go Vendor =>Go Module

  1. go PATH 弊端:无法实现多版本的控制
  2. go Vender 弊端:会存在依赖冲突
  3. go Module
    go.mod 是 go 管理第三方依赖的一种方式
    准确记录项目依赖:当前项目使用了哪些第三方包、依赖包的版本
    可重复构建:当前项目在任何环境或平台构建产物相同

2. 依赖管理三要素

  1. 配置文件,描述依赖 go.mod
  2. 中心仓库管理依赖库 Proxy
  3. 本地工具 go get/mod

3. 依赖配置

go module的下载问题: 比较常见的是GitHub,但是我们都知道github访问时不稳定的,所以用该平台可能会存在一些问题:

  1. 无法保证构建稳定性,可能存在增加、修改、删除软件版本。
  2. 无法保证依赖可用性,软件被删除
  3. 增加第三方压力,代码托管平台负载问题

Proxy是一个服务站点,通过它可以拉取依赖。

go get

go get example.org/pkg + 参数

  1. @update 默认参数
  2. @none 删除依赖
  3. @v1.1.2 指定tag版本,语义版本
  4. @23dfdd5 特定的commit版本
  5. @master 指定分支的最新commit

go mod

go mod + 参数

  1. init         初始化,创建go.mod文件
  2. download 下载模块到本地缓存
  3. tidy 增加需要的依赖,删除不需要的依赖

学习记录使用