csp
CSP(Communicating Sequential Processes)是Go语言并发模型的基础,有两种实现方式:
- 通信共享内存:通过通道(channel)进行通信,保证顺序。
- 共享内存实现通信:通过加锁等方式实现共享内存的访问。
Go语言更推荐使用通道来进行通信,因为这种方式更安全、更易于管理。
分两种,
第一种涉及到一个重要的概念,叫做通道
把协程做了个连接,来保证顺序
第二种必须是通过物质量对内存进行一个加锁,需要获得临界区的一个权限,在这种机制下不同的空间就容易发生数据静态的问题,就是一定程度上会影响程序的性能
通过两个的对比,go语言提倡用第一种来实现共享内存
通道
通道分为两种一种是无缓冲通道,一种是有缓冲通道
make(chan 元素类型,[缓冲大小])
根据是否有缓冲区的大小来判断有无缓冲通道
无缓冲通道 make(chan int),被称为同步通道
有缓冲通道 make(chan int, 2)
通道的容量,代表通道能存放多少元素了,类比于学校的仓库的格子,是有限的,如果格子满了就装不下了,这种情况下会发生阻塞发送,知道有人取走才能继续放下去
具体使用
消费者的消费速度比打印可能更加复杂
消费者的消费速度可能相对慢一些
生产者的逻辑相对简单可能相对较快
我们用带缓冲的一个队列(通道)就不会因为消费者的一个问题来影响整个的一个效率
我们每一个channel都用了defer做了一个延迟的资源关闭
通道可以用于生产者-消费者模式,确保生产者和消费者之间的数据同步。例如:
go
func producer(ch chan
int) {
for i := 0; i <
5; i++ {
ch <- i
}
close(ch)
}
func consumer(ch chan
int) {
for num := range
ch {
fmt.Println
("Consumed:",
num)
}
}
func main() {
ch := make(chan
int, 2)
go producer(ch)
consumer(ch)
}
计时器
Add(delta int):增加计数器的值。
Done():减少计数器的值。
Wait():阻塞直到计数器为0。
刚才用的例子都是用的sleep来进行操作进行阻塞,这个并不是优雅的,因为我们不知道确切的知道字协程执行的时间,就无法精确的设置sleep的时间。
go语言中其实可以使用WaitGroup,来实现并发任务的一个同步
三个方法
Add(dekta int)计数器+dekta
Done() 计数器-1
Wait() 阻塞直到计数器为0
所以最开始讲协程的例子可以变为
func H(){
var wg sync.WaitGroup
wq.Add(5);
for i:=0 ; i < 5;i++{
go func ( j int){
defer wg,Done()
hello(j)
}(i)
}
wg.Wait()
}
二:依赖管理
GOPATH
1 GOPATH 是一个环境变量
有三个重点
bin ,pkg ,src
2 它的项目代码所有的依赖与src下的代码
3 从go get 下载最新版本的包到src目录下
这样的话会存在一些弊端,无法实现package的多版本控制,
因为他们用的时同一个src的源码
GOVander
在项目目录中会添加vendor文件,所有的依赖包都会放到vendor的文件夹中,在引入依赖的副本时优先从vandor里面去寻找,如果找不到再回到GOPATH中。
这样v1找到的是v1版本,v2找到的是v2版本
这样就解决了多个项目需要同一个package依赖的冲突问题
Go Vender的弊端
他其实也是依赖的项目的一个源码,并不能很清晰的标识一个版本的概念
Go Module
go module解决了版本等依赖问题
go module首先是用go.mod文件对依赖包版本进行管理(用来配置文件,描述依赖)
然后通过go get/go mod 指令工具来管理依赖包(本地工具)
它的目的是,定义 版本的规则和项目依赖的关系
以来配置
version
GOPATH和GOVander都是源码副本的依赖,没有版本的定义
而module有自己的版本规则
语义话版本
#{MAJOR}#{MINOR}#{PATCH}
MAJOR是一个大版本,可以是不兼容的,不同MAJOR间其实是有代码隔离的
MINOR是新增函数和功能,需要在MAJOR下做到前后兼容
PATCH一般是做一些代码bug的修复
例子V1.3.0和V2.3.0
另一个版本规则是基于commit 伪版本
每次提交代码或者提交commit,go都会形成一个伪版本号