-
channel是多个协程之间通讯的管道
-
一端发送数据,一端接受数据
-
同一时间只有一个携程可以访问数据,无共享内存模式可以出现的内存竞争
-
并发并行
并发(concurrency): 两个或多个事件在同一时间间隔发生 并行(parallellism): 两个或多个时间在同一时刻发生
channel单线程消费者生产者模型
思路
生产者端:写入数据写入缓冲区 消费者端:从缓冲区读取数据 缓冲区:公共区(商场快递中心)一边放,一边取,但共同点是必须都来到商场或快递中心对数据进行处理。 由生产者,消费者,缓冲区组成的模型称为,生产者消费者模型。 生产者不断放入数据,消费者不断接收数据 当channel中没有数据时,消费者端将会阻塞,等待生产者放入数据。
为什么不让消费者直接从生产者端取数据?设置缓冲区的好处是?
将整个生产者消费者想象成寄快递的过程,生产者是寄件方,消费者是收件方,缓冲区是快递中心。如果没有快递中心,寄件方和收件方能连接吗? 可以,但一旦任意一方发生改变,则需重新连接,也影响了对应的寄/收的。所以经过缓冲区,可以让寄收两方互不影响,降低耦合度。 同时,消费者直接从生产者端取数据也有另一个弊端,函数调用是同步的(阻塞的),当其中一方没有返回前,另一方只好一直等待,无端浪费时间。而添加缓冲区,生产者和消费者是两个独立的并发主体(并发:两个或多个事件在同一时间间隔发生)生产者把快件一丢在缓冲区,就能马上着手下一件快件的准备,不用等待消费者接收,在这个时候增加生产者端(寄件人)数量,将发送的快递数量寄存在快递中心,也不会影响消费者端,提高了并发能力。 当生产者(寄件人)一次性发出过多,消费者不能无法一次性接收过多数据(快件),则可以暂存在缓冲区,等待下一次取回
缓冲区的好处
-
1、解耦:降低生产者和消费者之间的耦合度(耦合度,两个模块之间的关联程度)
-
2、并发能力提高:生产者和消费者数量不对等,仍能保持正常通信
-
3、缓存:生产者和消费者数据处理速度不一致时,可以暂存数据
通过channel充当缓冲区,根据需求选择使用有缓冲/无缓冲通道
有缓冲:要求异步通信 无缓冲:要求同步通信
单线程生产者消费者模型
package main
import (
"fmt"
"time"
)
func consumer(in <-chan int) {
for num := range in {
fmt.Println("消费者得到数据:", num)
time.Sleep(time.Second)
}
}
func producer(out chan<- int) {
for i := 0; i < 10; i++ {
data := i * i
fmt.Println("生产者生产数据:", data)
out <- data // 数据写入缓冲区
}
close(out) //关闭管道
}
func main() {
//用channel来传递"产品", 不再需要自己去加锁维护一个全局的阻塞队列
ch := make(chan int, 5) // 添加缓冲区,5,若改成无缓冲ch := make(chan int) ,变成并行操作,生产者等待消费者sleep后才能继续写入数据
go producer(ch) // 子go程作为生产者
consumer(ch) // 主go程作为消费者
}
模拟订单
package main
import (
"fmt"
"time"
)
type OrderInfo struct { //创建结构体类型OrderInfo
id int //模拟订单编号
}
func consumer2(in <-chan OrderInfo) { 订单处理,生产者
for order := range in { //从channel取出订单
fmt.Println("订单id:", order.id) //模拟处理订单
time.Sleep(time.Second)
}
}
func producer2(out chan<- OrderInfo) { //生成订单,生产者
for i := 0; i < 10; i++ { //循环生成10份订单
order := OrderInfo{id: i + 1}
fmt.Println("生产者生产数据:", order)
out <- order // 数据写入缓冲区
}
close(out) //关闭管道
}
func main() {
//用channel来传递"产品", 不再需要自己去加锁维护一个全局的阻塞队列
ch := make(chan OrderInfo) // 添加缓冲区,5,若改成无缓冲ch := make(chan OrderInfo) ,变成并行操作,生产者等待消费者sleep后才能继续写入数据
go producer2(ch) // 子go程作为生产者
consumer2(ch) // 主go程作为消费者
}