这是我参与「第三届青训营 -后端场」笔记创作活动的的第 3 篇笔记
并发编程-基础
Goroutine
协程,又叫轻量级线程。与传统的系统级线程和进程相比,协程最大的优势在于“轻量级”。可以轻松创建上万个而不会导致系统资源衰竭。而线程和进程通常很难超过1万个。这也是协程别称“轻量级线程”的原因。
一个 线程 中可以有任意多个 协程,但某一时刻只能有一个协程在运行,多个协程分享该线程分配到的计算机资源。
Go 在语言级别支持协程,叫 goroutine。Go 语言标准库提供的所有系统调用操作(包括所有同步IO操作),都会出让CPU给其他 goroutine。这让轻量级线程的切换管理不依赖于系统的线程和进程,也不需要依赖于CPU的核心数量。
Go 语言为并发编程而内置的上层API基于顺序通信进程模型 CSP(communicating sequential processes)。这就意味着显式锁都是可以避免的,因为Go通过相对安全的通道发送和接受数据以实现同步,这大大地简化了并发程序的编写。
CSP 模型是上个世纪七十年代提出的,用于描述两个独立的并发实体通过共享的通讯 channel(管道) 进行通信的并发模型。 CSP 中 channel 是第一类对象,它不关注发送消息的实体,而关注与发送消息时使用的 channel。
Channel
channel 是被单独创建并且可以在进程之间传递,类似于管道,它的通信模式类似于 boss-worker 模式的,一个实体通过将消息发送到channel 中,然后又监听这个 channel 的实体处理,两个实体之间是匿名的,这个就实现实体中间的解耦,其中 channel 是同步的一个消息被发送到 channel 中,最终是一定要被另外的实体消费掉的,在实现原理上其实是一个阻塞的消息队列。
/*
定义一个channel时,也需要定义发送到channel的值的类型。channel可以使用内置的make()函数来创建:
chan是创建channel所需使用的关键字。Type 代表指定channel收发数据的类型。
*/
make(chan Type) //等价于make(chan Type, 0)
make(chan Type, capacity)
/*
channel非常像生活中的管道,一边可以存放东西,另一边可以取出东西。
channel通过操作符 <- 来接收和发送数据,发送和接收数据语法:
*/
channel <- value //发送value到channel
<-channel //接收并将其丢弃
x := <-channel //从channel中接收数据,并赋值给x
x, ok := <-channel //功能同上,同时检查通道是否已关闭或者是否为空
Sync
依赖管理
Go 依赖管理演进: GOPATH -> Go Vender -> Go Module
GOPATH
- 环境变量 $GOPATH。
- 项目代码直接依赖 src 下的代码。
- 通过 go get 下载最新版本的包到 src 目录下。
弊端
- 项目必须写在 GOPATH 下。
- 没有内置的对扩展包的版本追踪。当项目 A 和项目 B 依赖于某一 package 的不同版本时,无法实现 package 的多版本控制。
- 执行 go get 时,无法通过指定来拉取指定的版本
Go Vendor
- 依赖于 GOPATH
- 项目目录下增加 vendor 文件,将所有依赖包副班形式放在 $ProjectRoot/vendor 下。编译的时候将从 vendor 目录中查找依赖而不从 GOPATH/src 中对应目录中查找。
- 解决了多个项目需要同一个 package 依赖的冲突问题。
弊端
- 无法控制依赖的版本。
- 更新项目容易出现依赖冲突,导致编译出错。
Go Module
- 通过 go.mod 文件管理依赖包版本
- 通过 go get/go mod 指令工具管理依赖包
- 可以定义版本规则和管理项目依赖关系
依赖管理三要素
- 配置文件, 描述依赖 (go.mod)
- 中心仓库管理依赖库 (Proxy)
- 本地工具 (go get/mod)
工具
go get example.org/pkg
- @update 默认
- @none 删除依赖
- @v1.1.2 tag 版本, 语义版本
- @23dfdd5 特定的 commit
go mod
- init 初始化 go.mod 文件
- download 下载模块到本地缓存
- tidy 增加需要的依赖, 删除不需要的依赖