当前位置: 首页 > article >正文

深入学习Go-7 Channel

Go语言实现了CSP的并发编程模式,即不要通过共享内存来通信,而要通过通信来实现内存共享。channel就是各groutine之间通信的管道。

Channel读写逻辑

channel是一个结构体类型,其中包括数据缓冲区、等待读取的队列recvq和等待写入的队列sendq。

写入数据

当向channel中写入数据时,一般有三种情况:

1,写入数据时,当recvq队列不为空,说明缓冲区为空或着没有缓冲区,直接从recvq队列中取出等待读取的goroutine,写入数据并唤醒,写入过程结束。

2,如果recvq队列为空且缓冲区有空余空间,直接写入数据到缓冲区,写入过程结束。

3,如果recvq队列为空且缓冲区没有空余空间,数据写入到当前的goroutine并放入到sendq队列,进入休眠状态,等待被读取的goroutine唤醒。

读取数据

1,读取数据时,当sendq队列不为空且没有缓冲区,直接从sendq中取出等待写入的goroutine,从中读取数据并唤醒,结束读取过程。

2,如果sendq队列不为空且缓冲区已满,从缓冲区的头部读取数据。从sendq中取出等待写入的goroutine,将待读取的数据写入缓冲区尾部并唤醒,结束读取过程。

3,如果sendq队列为空且缓冲区中有数据,从缓冲区的头部读取数据,结束读取过程。

4,如果sendq队列为空且缓冲区中没有数据或没有缓冲区,将当前的goroutine放入到recvq队列,进入休眠状态,等待被写入的goroutine唤醒。

Channel读写特性

1,向一个nil channel发送数据,或者从一个nil channel读取数据,当前goroutine会阻塞。

2,向一个已经关闭的channel发送数据,会引起panic。

3,从一个已经关闭的channel读取数据,如果缓冲区为空,则返回类型的零值。

4,关闭一个已经关闭的channel,会引起panic。

优雅关闭Channel

当一个channel关闭时,其他goroutine向这个channel发送数据或再次关闭时,都会发生panic。

如何优雅的关闭channel,一个原则就是不要在receiver处关闭channel,也不要在多个sender处关闭channel。

我们重点看多个sender的情况:

多个 sender,一个 receiver

对于有多个sender,一个reciver的情况,需要增加一个额外用于传递close的channel。reciever调用close关闭channel,多个sender同时收到close信号,停止发送数据。例子如下:

func main() {rand.Seed(time.Now().UnixNano())dataChan := make(chan int, 10)stopChan := make(chan chan struct{})for i := 0; i < 5; i++ {go func(i int) {for {select {case <-stopChan:fmt.Printf("协程: %d 收到停止发送数据的信号\n", i)returncase dataChan <- rand.Intn(10):}}}(i)}go func() {for data := range dataChan {if data == 9 {fmt.Println("发送停止信号 ... ")close(stopChan)return}fmt.Println(data)}}()ch := make(chan os.Signal, 1)signal.Notify(ch, os.Interrupt)<-ch
}

多个 sender,多个 receiver

对于有多个sender,多个receiver的情况,除了需要增加一个用于传递close的channel,还需要一个再增加一个信号channel。

创建一个goroutine监听这个中间channel,sender或receiver发送关闭信号给这个中间channel,当收到关闭信号后关闭传递close的channel,这时多个sender或多个receiver收到close后,停止发送数据和停止接收数据。例子如下:

func main() {rand.Seed(time.Now().UnixNano())dataChan := make(chan int, 10)stopChan := make(chan struct{})toStop := make(chan string, 10)go func() {signal := <-toStopfmt.Println("停止信号: " + signal)close(stopChan)}()for i := 0; i < 5; i++ {go func(i int) {for {data := rand.Intn(10)if data == 9 {toStop <- "close signal by sender#" + strconv.Itoa(i)return}select {case <-stopChan:fmt.Printf("发送数据的协程: %d 收到停止发送数据的信号\n", i)returncase dataChan <- rand.Intn(10):}}}(i)}for i := 0; i < 5; i++ {go func(i int) {for {select {case <-stopChan:fmt.Printf("接收数据的协程: %d 收到停止发送数据的信号\n", i)returncase data := <-dataChan:if data == 9 {toStop <- "close signal by receiver#" + strconv.Itoa(i)return}}}}(i)}ch := make(chan os.Signal, 1)signal.Notify(ch, os.Interrupt)<-ch
}

总结

在这里我们主要讲了Channel的读写逻辑及读写特性;优雅关闭Channel的原则就是不要在receiver处关闭channel,也不要在多个sender处关闭channel。


更多【分布式专辑】【架构实战专辑】系列文章,请关注公众号

图片

 

http://www.lryc.cn/news/2418324.html

相关文章:

  • 爬过这些网站才算会爬虫
  • oracle database filesystem (DBFS) 简单配置文档
  • 10个好用的免费图片网站,绝对能在2021年设计上好帮手
  • (转)新民周刊:3Q大战始末
  • Cisco3750G和H3C S5024P端口汇聚做VLAN trunk案例
  • [玩转BLE]cc2640广播数据格式简介
  • 乾坤(qiankun)的使用
  • amend用法 git 信息_看了这篇,我确定你已经彻底搞懂Git了
  • Ajax——Ajax实现自动补全
  • Spring注解@Scope
  • BPMN 2.0规范详解
  • Mutual Information 互信息的应用
  • 【教程】如何为自己的小程序添加统计工具
  • Vlan和Trunk配置
  • 数据可视化(二):犯罪案件分析
  • 2022美赛题目
  • android_button onclick点击事件的5种写法
  • Matlab在自动控制领域中的应用
  • 网址导航
  • 合宙ESP32C3 Arduino 初探教程
  • 上网行为网络管理系统 (2024年最强行为管理软件科普)
  • Cloudflare + 远程浏览器隔离
  • 在自己的网页中iframe别人的电子地图
  • 开发者的职场成长路径
  • APACHE服务器httpd.exe进程占用cpu100%的解决方法
  • UWB芯片介绍
  • 永磁同步电机表贴式和嵌入式
  • 吴晓波:预见2021(跨年演讲 —— 02 “云上中国”初露峥嵘)
  • python爬虫进阶(二):动态网页爬虫
  • Win11系统提示找不到ngentasklauncher.dll文件的解决办法