channel是GO语言并发体系中的主推的通信机制,它可以让一个 goroutine 通过它给另一个 goroutine 发送值信息。每个 channel 都有一个特殊的类型,也就是 channels 可发送数据的类型。一个可以发送 int 类型数据的 channel 一般写为 chan int。Go语言提倡使用通信的方法代替共享内存,当一个资源需要在 goroutine 之间共享时,通道在 goroutine 之间架起了一个管道,并提供了确保同步交换数据的机制。声明通道时,需要指定将要被共享的数据的类型。可以通过通道共享内置类型、命名类型、结构类型和引用类型的值或者指针。
channel是一种特殊的类型是保证协程安全的,也就是在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。而且遵循先入先出(First In First Out)的规则,保证收发数据的顺序。这两个特性是channel可以产生共享内存功能的重要原因。。读完这一讲,下面我们就可以继续我们的例子,开始GO语言并发的实战了
一、通道的声明
1.经典方式声明
通过使用chan类型,其声明方式如下:
var name chan type
其中type表示通道内的数据类型;name:通道的变量名称,不过这样创建的通道只是空值 nil,一般来说都是通道都是通过make函数创建的。
2.make方式
make函数可以创建通道格式如下:
name := make(chan type)
3.创建带有缓冲的通道
后面会讲到缓冲通道的概念,这里先说他的定义方式
name := make(chan type, size)
其中type表示通道内的数据类型;name:通道的变量名称,size代表缓冲的长度。
二、通道的数据收发
1. 通道的数据发送
通道的发送的操作符<-,将数据通过通道发送的格式为:
chan <- value
注意,如果将数据发送至一个无缓冲的通道中,如果数据一直都没有接收,那么发送操作将持续阻塞。但是GO的编译器能够发现明显的错误,比如
ch := make(chan int) // 创建一个整型通道
ch <- 0// 尝试将0通过通道发送
编译时会报错:fatal error: all goroutines are asleep - deadlock!
2.通道的数据接收
通道接收数据的操作符也是<-,具体有以下几种方式
1) 阻塞接收数据
阻塞模式接收数据时,将接收变量作为<-操作符的左值,格式如下:
data := <-ch
执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。
如需要忽略接收的数据,则将data变量省略,具体格式如下:
<-ch
2) 非阻塞接收数据
使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下:
data, ok := <-ch
非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。一般只配合select语句配合定时器做超时检测时使用。
三、channel的超时检测
Go语言没有提供直接的超时处理机制,一般使用select关键字来设置超时。Select虽然不是专为超时而设计的,却能很方便的解决超时问题,因为select的特点是只要其中有一个 case 已经完成,程序就会继续往下执行,而不会考虑其他 case 的情况。select 的用法与 switch 语言非常类似,由 select 开始一个新的选择块,每个选择条件由 case 语句来描述。
但是与switch 语句相比,select 有比较多的限制,其中最大的一条限制就是每个 case 语句里必须是一个 IO 操作,结构如下:

select {case <-chan1:// 如果chan1成功读到数据,则进行该case处理语句case chan2 <- 1:// 如果成功向chan2写入数据,则进行该case处理语句default:// 如果上面都没有成功,则进入default处理流程
}

比如在示例代码中,我们创建了一个用于传数据的channel,和一个用于超时退出的通道quit,并使用select来接收channel的数据,其中如果程序运行到3s时,退出通道会被置为true,这时<-quit不再阻塞,主goroutine退出。

package mainimport ("fmt""time"
)func main() {ch := make(chan int)    //设置一个传送数据的channelquit := make(chan bool) //设置一个超时传送的channel//新开一个协程go func() {for {select {case num := <-ch: //如果收到传输能道ch中的值则打印.fmt.Println("num = ", num)case <-time.After(3 * time.Second): //如果达到3秒则认定超时fmt.Println("超时")quit <- true //将quit通道置为true}}}() //别忘了()for i := 0; i < 5; i++ {ch <- itime.Sleep(time.Second)}<-quit //当超时quit将不再阻塞,主协和将会退出fmt.Println("程序结束")/*运行结果为num =  0num =  1num =  2num =  3num =  4超时程序结束*/
}

四、通道数据收发的注意事项
1.通道的收发操作在不同的两个 goroutine 间进行。由于通道的数据在没有接收方处理时,数据发送方会持续阻塞,因此通道的接收必定在另外一个 goroutine 中进行。
2.接收将持续阻塞直到发送方发送数据。如果接收方接收时,通道中没有发送方发送数据,接收方也会发生阻塞,直到发送方发送数据为止。
3.每次只接收一个元素。
第4节 深入理解GO语言中的channel
GO语言中的有关chan的代码位置在GOPATH\src\runtime\chan.go,阅读代码可以发现channel 内部就是一个带锁的队列。
1.基本数据结构

type hchan struct {qcount   uint           // 队列中数据个数dataqsiz uint           // channel 大小buf      unsafe.Pointer // 存放数据的环形数组elemsize uint16         // channel 中数据类型的大小closed   uint32         // 表示 channel 是否关闭elemtype *_type // 元素数据类型sendx    uint   // send 的数组索引recvx    uint   // recv 的数组索引recvq    waitq  // 由 recv 行为(也就是 <-ch)阻塞在 channel 上的 goroutine 队列sendq    waitq  // 由 send 行为 (也就是 ch<-) 阻塞在 channel 上的 goroutine 队列// lock protects all fields in hchan, as well as several// fields in sudogs blocked on this channel.//// Do not change another G's status while holding this lock// (in particular, do not ready a G), as this can deadlock// with stack shrinking.    lock mutex
}

如果是一个无缓冲的chan只需要用一个 lock来确保无竞争冲突。而带缓冲的chan其实就是一个环形队列,通过sendx 和 recvx 分别用来记录发送、接收的位置。
2.数据发送的实现
其实数据发送和接收的逻辑比类似,这里我们只举数据发送的例子。

func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {if c == nil {//正确性检查if !block {return false}gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2)throw("unreachable")}if debugChan {print("chansend: chan=", c, "\n")}if raceenabled {racereadpc(c.raceaddr(), callerpc, funcPC(chansend))//重置竞争标志位}// Fast path: check for failed non-blocking operation without acquiring the lock.//// After observing that the channel is not closed, we observe that the channel is// not ready for sending. Each of these observations is a single word-sized read// (first c.closed and second c.recvq.first or c.qcount depending on kind of channel).// Because a closed channel cannot transition from 'ready for sending' to// 'not ready for sending', even if the channel is closed between the two observations,// they imply a moment between the two when the channel was both not yet closed// and not ready for sending. We behave as if we observed the channel at that moment,// and report that the send cannot proceed.//// It is okay if the reads are reordered here: if we observe that the channel is not// ready for sending and then observe that it is not closed, that implies that the// channel wasn't closed during the first observation.if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) ||(c.dataqsiz > 0 && c.qcount == c.dataqsiz)) {return false//如果队列被关闭等情况,返回错误}var t0 int64if blockprofilerate > 0 {t0 = cputicks()}lock(&c.lock)if c.closed != 0 {unlock(&c.lock)panic(plainError("send on closed channel"))}if sg := c.recvq.dequeue(); sg != nil {// Found a waiting receiver. We pass the value we want to send// directly to the receiver, bypassing the channel buffer (if any).send(c, sg, ep, func() { unlock(&c.lock) }, 3)return true}if c.qcount < c.dataqsiz {// Space is available in the channel buffer. Enqueue the element to send.qp := chanbuf(c, c.sendx)if raceenabled {raceacquire(qp)racerelease(qp)}typedmemmove(c.elemtype, qp, ep)c.sendx++if c.sendx == c.dataqsiz {c.sendx = 0}c.qcount++unlock(&c.lock)return true}if !block {unlock(&c.lock)return false}// Block on the channel. Some receiver will complete our operation for us.gp := getg()mysg := acquireSudog()mysg.releasetime = 0if t0 != 0 {mysg.releasetime = -1}// No stack splits between assigning elem and enqueuing mysg// on gp.waiting where copystack can find it.mysg.elem = epmysg.waitlink = nilmysg.g = gpmysg.isSelect = falsemysg.c = cgp.waiting = mysggp.param = nilc.sendq.enqueue(mysg)goparkunlock(&c.lock, waitReasonChanSend, traceEvGoBlockSend, 3)// Ensure the value being sent is kept alive until the// receiver copies it out. The sudog has a pointer to the// stack object, but sudogs aren't considered as roots of the// stack tracer.KeepAlive(ep)// someone woke us up.if mysg != gp.waiting {throw("G waiting list is corrupted")}gp.waiting = nilif gp.param == nil {if c.closed == 0 {throw("chansend: spurious wakeup")}panic(plainError("send on closed channel"))}gp.param = nilif mysg.releasetime > 0 {blockevent(mysg.releasetime-t0, 2)}mysg.c = nilreleaseSudog(mysg)return true
}

我们看到在发送数据时,首先获取lock,然后进行竞争检查、指针检查等,然后更新读写位置,记录数据,并释放锁,如果,当前hchan.buf 无可用空间,则将操作阻塞。
所以从本质上讲channel就是一个基于锁的循环队列。

一文读透GO语言的通道相关推荐

  1. c语言 char转int_图文并茂,一文讲透C语言结构体内存对齐

    ↑点击上方蓝色字体,关注"嵌入式软件实战派"获得更多精品干货. (以下有约5000字内容,建议收藏再读,推荐下载源码自行测试以加深理解.) 面试官:你知道C语言的结构体对齐吗? 应 ...

  2. c语言位向量机伞_一文读懂C语言精华-指针变量和指向指针的指针

    1978年贝尔实验室正式发表C语言,受到众多IT从业者的热捧,即使41年过去了,C语言仍然牢牢占据最受欢迎编程语言前二的位置,许多人的程序开发之路也是从C语言开始的. 编程语言受欢迎排名 C语言简洁紧 ...

  3. 为什么大厂都在用 GO 语言?读透 GO 语言的切片

    作者 | 马超 责编 | 夕颜 封图 | CSDN下载自视觉中国 出品 | CSDN(ID:CSDNnews) 今年3月初,腾讯发布了<腾讯研发大数据报告>,笔者发现GO语言的使用在鹅厂已 ...

  4. 一文读懂Java语言方法的重写(覆盖、Override)

    很多初学Java语言的小伙伴,在学到"面向对象"这块内容的时候,都会学到的一个概念,那就是"方法的重写".重写又叫覆盖,英文名为"Override&q ...

  5. 语言相关系数显著性_相关性分析在SPSS中的具体操作,一文读懂相关系数的含义及使用——【杏花开生物医药统计】...

    相关性分析介绍 生物和医学统计中,相关分析属于流程前端的探索性分析,研究变量间关系及性质,其结果在为下一步采取何种方法做出指引,为数据挖掘之前的基础工作. 相关系数的选择 相关分析之前,需要先确认变量 ...

  6. 7. 重磅硬核 | 一文聊透对象在JVM中的内存布局,以及内存对齐和压缩指针的原理及应用

    重磅硬核 | 一文聊透对象在JVM中的内存布局,以及内存对齐和压缩指针的原理及应用 大家好,我是bin,又到了每周我们见面的时刻了,我的公众号在1月10号那天发布了第一篇文章?<从内核角度看IO ...

  7. 人工智能(8)---一文读懂人工智能产业链:基础技术、人工智能技术及人工智能应用

    一文读懂人工智能产业链:基础技术.人工智能技术及人工智能应用 概要:针对人工智能产业链,主要有三个核心:基础技术.人工智能技术及人工智能应用,本文将从主要从这三个方面进行梳理 人工智能(Artific ...

  8. 一文读懂JS继承相关知识点

    一文读懂JS继承相关知识点 Javascript 面向对象编程(一):封装 一. 生成实例对象的原始模式 二. 原始模式的改进 三. 构造函数模式 四.构造函数模式的问题 五. Prototype模式 ...

  9. 一文通透从输入URL到页面渲染的全过程----高频面试

    一文通透从输入URL到页面渲染的全过程----高频面试 喜欢大海 喜欢夕阳 写下便是永恒 文章目录 一文通透从输入URL到页面渲染的全过程----高频面试 重温 进程与线程 什么是进程 什么是线程 进 ...

最新文章

  1. java 穷举 排列组合,JavaScript递归穷举所有排列组合并找出重复值
  2. Mr. Main and Windmills 模拟,计算几何(昆明)
  3. 省编码市编码区县编码_无浪费编码
  4. java office在线编辑_国外10个最受欢迎的 Java 开发的 CMS 系统
  5. 力扣151. 翻转字符串里的单词
  6. 在VS 2010上搭建Windows Phone 7开发平台
  7. 人工智能(10)---机器学习知识体系篇(初级篇,中级篇,高级篇)
  8. java笔记高级部分
  9. optuna 自动化调参利器
  10. python控制mt4自动交易软件下载_MT4 EA智能自动交易系统使用教程
  11. PCWorld:流量日趋集中 大公司影响整个互联网
  12. 电脑提示Wtautoreg.exe无法找到入口怎么解决?
  13. KConfig、Makefile详解以及ARM平台Linux内核的编译
  14. JavaScript点击button更改内容,清空页面
  15. 【Bugzilla】我按照bugzilla的官方指导进行的安装。(一)
  16. java 1029: 三角形判定
  17. java毕业生设计医院分诊管理系统计算机源码+系统+mysql+调试部署+lw
  18. 大学生访谈计算机教师,职业生涯人物访谈报告,教师(共10篇)
  19. [网鼎杯 2020 玄武组]SSRFMe【redis主从复制ssrf】
  20. strtol函数的使用

热门文章

  1. 2019牛客多校训练营第一场 E题 ABBA 题解
  2. 进不去chatgpt来加入新必应new bing吧,怎么申请newbing,出错了怎么解决
  3. 与 SQL Server 2012 建立连接时出现与网络相关的或特定于实例的错误。
  4. python中步长什么意思_python步长什么意思【Python教程】,Python,step,步长
  5. 聊天机器人—简介(一)
  6. 你处在人生的哪个阶段
  7. NISP二级题库总结
  8. Infortrend主要产品线全部支持25 GbE主机通道
  9. 关于MP4文件格式解析
  10. 火爆国外的生食饮食法,不允许你不知道