这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
协程
协程不等于线程,看上去这两个东西好像是同一个东西,达到的效果也相同,但是由于底层的实现上有区别,协程更加轻量,可以比线程跑的更多,Go 在高并发中使用的就是协程
在 Go 中,这个叫做 Goroutine,使用协程也很简单,只需要在调用函数前面加上 go 就行
for i := 0; i < 5; i++ {
go fun(i)
}
这样就创建了 5 个协程,他们会像多线程一样分别自己管自己跑,不会在 fun(1) 结束之后才跑 fun(2)
既然协程比线程轻量,那么可以把线程优化掉吗?
并不行。事实上,线程和协程表面上看起来差不多,但是本质是不一样的。通俗地讲,线程使用的是操作系统级别的资源,而协程是调用程序自身所在的线程,也叫用户级别的资源。操作系统的资源是非常宝贵的资源,是有限的,因此能开的线程就相对协程较少
下图就能方便理解这两个概念了
信道
熟悉多线程的朋友一定知道既然不是串行,那么一定需要通信,一般的语言都采取 通过内存通信,而 Go 是反过来的,采取 通信来交换内存,这样做同样能减少资源的消耗,所以 Go 语言的设计师相当注重在低配环境下运行程序啊(?
在 Go 语言中,信道(channel,也可以叫通道)就充当了 goroutine 之间连接工具的角色
和 goroutine 一样,信道的创建也是非常简单,创建一个信道只需 make(chan 数据类型) 即可
ch := make(chan int)
信道主要的操作有两种:发送、接收。同时还有一个关闭操作
// 发送 x 到信道中
ch <- x
// 接收信道中的值
x = <-ch
// 接收信道,但是丢弃结果
<-ch
// 不使用信道了,关闭信道
close(ch)
一般情况下,信道是没有缓存的,这就导致了如果信道里有东西,那么发送的那一方就无法王信道中发送,处于一个阻塞的状态,整个程序就停在那里,等待信道有空间为止才会继续
像下面的图片那样,供过于求,goroutine2 无法将数据放进去,于是就停在那里了
这非常影响程序的效率,为了防止这种现象的发生,信道也可以设置上缓存,设置缓存的方法是 make(chan int, 容量)
ch := make(chan int, 5)
这样 channel 就有足够的空间让 goroutine 可以先把数据放在 channel 的缓存中,继续手头的工作生产数据,真的是很资本家呢!
看到这两个 Go 的东西,感觉 Go 的程序会有很多优化空间,比如说要设置多大的缓存不会产生浪费也不会产生阻塞、要跑多少个 goroutine 才能让效率最大,这对测试来说无疑是一项很大工作量,不过大多数程序员是不会那么追求效率的吧(