《制坯系列-Golang专题》:chan作为协程之间通信的重要方式,是替代内存共享的最佳通信方式,本文对基本原理和关键知识点做简单介绍


chan底层数据结构

type hchan struct {qcount   uint           // 当前队列中剩余元素个数dataqsiz uint           // 环形队列长度,即可以存放的元素个数buf      unsafe.Pointer // 环形队列指针elemsize uint16         // 每个元素的大小,用于在buf中定位元素位置。closed   uint32         // 标识关闭状态elemtype *_type         // 元素类型,用于数据传递过程中的赋值sendx    uint           // 队列下标,指示元素写入时存放到队列中的位置recvx    uint           // 队列下标,指示元素从队列的该位置读出recvq    waitq          // 等待读消息的goroutine队列sendq    waitq          // 等待写消息的goroutine队列lock mutex              // 互斥锁,chan不允许并发读写
}

缓存区环形队列

  • dataqsiz指示了队列长度为6,即可缓存6个元素;
  • buf指向队列的内存,队列中还剩余两个元素;
  • qcount表示队列中还有两个元素;
  • sendx指示后续写入的数据存储的位置,取值[0, 6);
  • recvx指示从该位置读取数据, 取值[0, 6);

等待队列

阻塞:

  • 如果从缓冲区为空或则没有缓冲区的channel中读取数据,当前协程会加入recvq队列
  • 如果向缓冲区已满或则没有缓冲区的channel中写入数据,当前协程会加入sendq队列

 唤醒:

  • 因读取阻塞的协程会被向channel中写入数据的协程唤醒
  • 因写入阻塞的协程会被从channel中读取数据的协程唤醒


chan的操作

chan的创建

golang创建chan的代码示例

var ch1 chan int               // 声明,值为nil
var ch2 = make(chan int)       // 创建,不带缓冲区
var ch3 = make(chan int, 1)    // 创建,带缓冲区

chan的底层创建:创建channel的过程实际上是初始化hchan结构

func makechan(t *chantype, size int) *hchan {var c *hchanc = new(hchan)c.buf = malloc(元素类型大小*size)c.elemsize = 元素类型大小c.elemtype = 元素类型c.dataqsiz = sizereturn c
}

向channel写数据

  1. 如果等待接收队列recvq不为空,说明缓冲区中没有数据或者没有缓冲区,此时直接从recvq取出G,并把数据写入,最后把该G唤醒,结束发送过程;
  2. 如果缓冲区中有空余位置,将数据写入缓冲区,结束发送过程;
  3. 如果缓冲区中没有空余位置,将待发送数据写入G,将当前G加入sendq,进入睡眠,等待被读goroutine唤醒;

从channel读数据

  1. 如果等待发送队列sendq不为空,且没有缓冲区,直接从sendq中取出G,把G中数据读出,最后把G唤醒,结束读取过程;
  2. 如果等待发送队列sendq不为空,此时说明缓冲区已满,从缓冲区中首部读出数据,把G中数据写入缓冲区尾部,把G唤醒,结束读取过程;
  3. 如果缓冲区中有数据,则从缓冲区取出数据,结束读取过程;
  4. 将当前goroutine加入recvq,进入睡眠,等待被写goroutine唤醒;

关闭channel

关闭channel时会把recvq中的G全部唤醒,本该写入G的数据位置为nil。把sendq中的G全部唤醒,但这些G会panic。


channel操作中的阻塞和panic

阻塞

  • 从nil管道中读取数据
  • 从无缓冲区或则缓冲区已满的管道中读取数据
  • 向nil管道中写入数据
  • 向无缓冲区或则缓冲区已满的管道中写入数据

panic

  • 位于协程中sendq队列中的协程会在关闭channel时触发panic
  • 关闭值为nil的管道
  • 关闭已被关闭的管道
  • 向已经关闭的管道写入数据

管道的常见用法

单向channel

func readChan(chanName <-chan int) {    // 通过形参限定函数内部只能从channel中读取数据<- chanName
}
func writeChan(chanName chan<- int) {    // 通过形参限定函数内部只能向channel中写入数据chanName <- 1
}
func main() {var mychan = make(chan int, 10)writeChan(mychan)readChan(mychan)
}

select

使用select可以监控多channel,比如监控多个channel,当其中某一个channel有数据时,就从其读出数据。

case的选择为随机,如果所有case均阻塞则选择default,如果没有default则select阻塞

package main
import ("fmt""time"
)
func addNumberToChan(chanName chan int) {for {chanName <- 1time.Sleep(1 * time.Second)}
}
func main() {var chan1 = make(chan int, 10)var chan2 = make(chan int, 10)go addNumberToChan(chan1)go addNumberToChan(chan2)for {select {case e := <- chan1 :fmt.Printf("Get element from chan1: %d\n", e)case e := <- chan2 :fmt.Printf("Get element from chan2: %d\n", e)default:fmt.Printf("No element in chan1 and chan2.\n")time.Sleep(1 * time.Second)}}
}

for-range

通过range可以持续从channel中读出数据,好像在遍历一个数组一样,当channel中没有数据时会阻塞当前goroutine,与读channel时阻塞处理机制一样。

func chanRange(chanName chan int) {for e := range chanName {fmt.Printf("Get element from chan: %d\n", e)}
}

制坯系列-Golang专题-chan相关推荐

  1. 1112: 进制转换(函数专题)

    1112: 进制转换(函数专题) 时间限制: 1 Sec 内存限制: 128 MB 提交: 3448 解决: 2599 [提交] [状态] [讨论版] [命题人:admin] 题目描述 输入一个十进制 ...

  2. ZZULIOJ 1112: 进制转换(函数专题)

    进制转换(函数专题) 题目描述 输入一个十进制整数n,输出对应的二进制整数.常用的转换方法为"除2取余,倒序排列".将一个十进制数除以2,得到余数和商,将得到的商再除以2,依次类推 ...

  3. golang 学习 - chan以及chan的一下用例

    golang 学习 - chan 1. 通道 // _通道_ 是连接多个 Go 协程的管道.你可以从一个 Go 协程 // 将值发送到通道,然后在别的 Go 协程中接收.package mainimp ...

  4. golang的chan(管道)

    golang的chan翻译成中文就是管道,顾名思义,就是管道的一端用来读,另一端用来写,这与write和read函数的性质是非常相似的,比如说管道中没数据,就会发生读阻塞,管道中数据是满的,就会发生写 ...

  5. CP2K+Gaussian+LAMMPS+ReaxFF计算材料化学系列四大专题。

    背景: CP2K是一款较为强大的AIMD计算程序,免费开源,可高效并行.由于CP2K在做DFT时可以速度非常快地计算成千上万个原子的体系,因此在大规模模拟体系中经常被一些学者用到.比如计算原子数达到一 ...

  6. 【ZZULIOJ】1112: 进制转换(函数专题)

    ZZULIOJ题解 1112: 进制转换(函数专题) 题目描述 输入一个十进制整数n,输出对应的二进制整数.常用的转换方法为"除2取余,倒序排列".将一个十进制数除以2,得到余数和 ...

  7. golang管道chan

    package mainimport ("fmt""time" )func main() {//test2()test4()time.Sleep(time.Se ...

  8. golang基础-chan的select操作、定时器操作、超时控制、goroutine中使用recover

    chan的只读和只写 a.只读chan的声明 Var 变量的名字 <-chan int Var readChan <- chan int b. 只写chan的声明 Var 变量的名字 ch ...

  9. [易学易懂系列|golang语言|零基础|快速入门|(一)]

    golang编程语言,是google推出的一门语言. 主要应用在系统编程和高性能服务器编程,有广大的市场前景,目前整个生态也越来越强大,未来可能在企业应用和人工智能等领域占有越来越重要的地位. 本文章 ...

最新文章

  1. Mac下编译ffmpeg出现“ERROR: x265 not found using pkg-config”
  2. 读写xml节点的数据总结
  3. 用官方2012版本131兆,一共有四个自带软件
  4. react 使用 mobx_如何使用React和MobX状态树构建基于状态的路由器
  5. mysql数据库表名批量改为小写,MySQL 批量修改表名
  6. rabbitmq消息保证幂等的消息设计
  7. sqlserve生成随机数
  8. ArcGIS导出shape地图边界点数据
  9. python 画直方图
  10. php 双竖线,范数介绍,数字两边双竖线
  11. 计算机核心论文如何审稿,计算机核心期刊排名及投稿经验(范文).doc
  12. 突发!蚂蚁集团CEO宣布辞职,阿里方面表示属实!
  13. python-函数的递归
  14. CSS 网页背景图片设置
  15. 爬取北京链家二手房(requests和selenium)
  16. 用Python打造你的专属情人节贺卡,赶快发给TA浪漫一下吧
  17. 184. 部门工资最高的员工
  18. 3dmax和python做3d动画_ThingJS问答录 | 三维动画师和程序员的职业前景 3D 可视化
  19. 如何把录音转换成文字?这几个方法可以轻松解决录音转文字
  20. 易语言python模块_Python获取指定模块基址

热门文章

  1. iOS字体像数与磅的对应关系
  2. vue引入echarts-liquidfill水滴图并批量动态加载
  3. kali2022.1安装google chrome develop 专业版
  4. 解决win2008 R2远程桌面授权过期的方法
  5. available: expected at least 1 bean which qualifies as autowire candidate
  6. 含并行连结的网络 GoogLeNet / Inception V3 动手学深度学习v2 pytorch
  7. 类和对象(Java)
  8. nanopore测序技术专题(一):为什么要选择nanopore测序?
  9. 12.2 数据库控制文件复制到ASM磁盘组
  10. 《大数据》第1期“专题”——大数据与智慧城市