八、《go 并发编程》缓存通道

195 阅读2分钟
了解下

缓冲通道就是指一个通道,带有一个缓冲区。发送到一个缓冲通道只有在缓冲区满时才被阻塞。类似地,从缓冲通道接收的信息只有在缓冲区为空时才会被阻塞。 可以通过将额外的容量参数传递给make函数来创建缓冲通道,该函数指定缓冲区的大小。

代码看看
package main

import "fmt"

func main() {
	ch01 := make(chan int)
	// 默认都是 0 0
	// 非缓冲的通道可以理解为容量 = 0
	fmt.Println(len(ch01),cap(ch01))

	// 缓冲通道可以理解为有容量定义的通道
	ch02 := make(chan int,5)
	// 0 5
	fmt.Println(len(ch02),cap(ch02))

	// 这时候我们向缓冲通道写入一个数据
	ch02 <- 100
	// 长度 = 1 容量 = 5 缓冲区还有 4 个容量
	// 并且写入时没有被阻塞
	fmt.Println(len(ch02),cap(ch02))

	// 还有 4 个坑,那继续向通道写入 4 个数据
	ch02 <- 100
	ch02 <- 100
	ch02 <- 100
	ch02 <- 100
	// 长度 = 5 容量 = 5 缓冲区已经没有了容量了
	fmt.Println(len(ch02),cap(ch02))

	// 如果这时候作死,再向通道写一个数据
	ch02 <- 100
	// 就会遇到死锁了,fatal error: all goroutines are asleep - deadlock!
	// 因为这时候没有其他的来帮他解除阻塞了
}
执行
0 0
0 5
1 5
5 5
fatal error: all goroutines are asleep - deadlock!
特性分析

本质上,缓冲通道是在阻塞执行的时候建立一个缓冲区 当缓冲区空了的时候,写入就会进入阻塞,同样的读也会进入阻塞,最终造成死锁 所以应用场景上,只是在 channel 通道的基础上建立缓冲,来缓解阻塞的压力

看这个示例
package main

import (
	"fmt"
	"time"
)

var chA chan int

func main() {
	chA = make(chan int,10)

	// 子 goroutine 执行
	go sends()

	// 循环向通道 ch 读取数据
	for value := range chA {
		fmt.Println("主 goroutine 读取通道 chA 的值:",value)
	}
	fmt.Println("运行结束")
}

func sends() {
	for i := 1; i < 6; i++ {
		time.Sleep(1 * time.Second)
		fmt.Println("子 goroutine 向通道 chA 写入:",i)
		chA <- i
	}
	fmt.Println("关闭通道 ch ")
	close(chA)
}
执行的结果
主 goroutine 读取通道 chA 的值: 1
子 goroutine 向通道 chA 写入: 2
主 goroutine 读取通道 chA 的值: 2
子 goroutine 向通道 chA 写入: 3
主 goroutine 读取通道 chA 的值: 3
子 goroutine 向通道 chA 写入: 4
主 goroutine 读取通道 chA 的值: 4
子 goroutine 向通道 chA 写入: 5
关闭通道 ch 
主 goroutine 读取通道 chA 的值: 5
运行结束