Go 语言入门指南:基础语法和常用特性解析(并发) | 青训营

71 阅读3分钟

协程go

使用关键字go能快速启动一个并发运行的goroutine,goroutine底层时使用协程实现并发

go f(a int){
    fmt.Println(a)
}(1)

协程与线程实现的功能上差不多,重点在于协程它不“重”,比起动则几MB,工作在内核态的线程,几KB(用户态)的协程使用起来更加简洁,或许可以称之为“微线程”,因此在go开发过程中,goroutine它是一种可以随意挥霍的资源,只要你有使用的能力,你就可以随意开,而不是去担忧工具的重量反而会拖累业务。不过线程所需要在意的并发控制问题,协程仍然是有的,并且这也是使用过程中最大的难点

Go语言的CSP模型

主流语言中并发主要有两种方法

  • 通过共享内存来进行通信,不同线程之间是没有直接联系的,而是通过共享内存这个“中间人”来进行交互,同时这个“中间人”必须要加上一些保护,比如同步,或者加锁,由此可以预测一下,如果需要管理的中间变量足够多,涉及到的逻辑足够复杂,那管理起来可能会遇到不小的麻烦(例如死锁)
  • 通过通信来实现内存共享,软件工程提到共享内存属于第二级高耦合——公共耦合,因此放弃共享内存这一步,就做到了解耦的目的,但仍然会有数据竞争的问题存在,因此golang就定义了通道(channel)进行通信

channel通道

make(chan int)
make(chan int, 10)
<- chan
chan <-
  • 通道从某种意义上来说也是一种加锁,比如尝试占用某块数据时,就从channel中收取内存引用,收取成功,别人也就用不了了
  • 使用完后,将这内存块的引用发送到 channel,以此让给下一个人

其中channel又分有缓冲(可以看作“同步模式”)和无缓冲(可以看作“异步模式”)

sync

golang并没有完全抛弃上述的第一种方法,或许两者差距并没有所说的那么大

包依赖管理

GOPATH

GOPATH 是最早的构建模式,Golang 编译器会在 GOPATH 环境变量配置的本地路径下,查找 Golang 程序依赖的三方包,如果没有,则需要go get命令,不过go get 命令会自动下载最新的依赖包,如果有版本需求,项目会爆炸

Vendor

Golang 官方为了解决 go get 命令会下载最新版本依赖包的问题,后续引入 vendor 机制。所谓 vendor 机制,就是在 Golang 项目的目录中,创建一个目录名为 vendor 的目录,将 Golang 项目的所有依赖包缓存到该目录中。Golang 程序在编译时,Golang 编译器会优先在 vendor 目录中查找 Golang 程序依赖的三方包,而不是在 GOPATH 环境变量配置的本地路径下查找。但这无法应对更加复杂的依赖问题,以及依赖过多后,vendor目录的臃肿,影响 github 等的上传,并且目录必须在GOPATH 环境变量配置的本地路径下的 src 目录中。

Modules

使用 go module 管理依赖后会在项目根目录下生成两个文件 go.mod 和 go.sum

go.mod 中会记录当前项目的所依赖的包的信息

go.sum 记录每个依赖库的版本和哈希值,用来校验本地包的真实性

Goland 使用 go mod 时,一定要记得配置代理 (GO PROXY)