关于chan的思考
这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天 今天学习go语言的多并发的chan时引发了我对以往java编程多线程的思考。
以前在java的生产者与消费者模型中,通常会创建一个生产者和消费者共享的变量表示生产者生产物品的数量。
static public int nums = 0; //共享的变量
但是这样会导致一个比较严重的问题,nums的数量会减少到负数,这样是线程不安全的因此java中引入了synchronized给该内存上锁,但是这样会额外的消耗计算机的性能。而在go语言中又提供了另外一种解决这种线程不安全的办法。go语言中引入了chan类型。该类型是通过通信来共享内存的,而在java中是通过共享内存来通信,二者如下图所示。
在go中我们通过使用chan在不同的协程之间建立起通道拉共享数据。
chan的特性
- 同时只能由一个协程访问通道
- 通道的进出顺序遵循先进先出
chan的创建
chan可以分为两种无缓存通道和有缓存通道,有缓存就如同日常生活中的仓库一样生产速度大于消费速度就会将多余的产品放入仓库中,而其生产的数据将在缓存里形成队列等待取出。通道的声明形式如下
var ch chan int
chan本身是一个类型修饰符,其空值为nil需要配合make才能进行使用。
ch := make(chan int) //无缓存通道
ch1 := make(chan int, 3) //缓存长度为3的通道
使用通道接收数据
在创建通道之后就可以通过 **<-**向其发送数据了,发送的数据类型一定要与声明通道是的数据类型是一致的。
通道变量<-数据 // 发送数据给通道
数据的发送会持续到该数据被接收,若出现一些永远无法发送成功的数据go会智能识别出并作出提示,如以下例子
ch := make(chan int)
ch <- 0
上述代码的运行结果fatal error: all goroutines are asleep - deadlock! 此时所有 协程 中的 channel 并没有形成发送和接收对应的代码。
通道接受的四种写法
//1 阻塞接收数据
data := <-ch
//2 非阻塞接收数据
data, ok := <-ch
//3 接受任意数据并忽略接收的数据
<-ch
//4 循环接受数据
for data := range ch {
}