Go 信道Channel
信道(Channel)
信道(Channel)可以被认为是协程之间通信的管道。数据可以从信道的一端发送并在另一端接收。
默认为同步模式,需要发送和接收配对。否则会被阻塞,直到另外的信道准备好后被唤醒。
信道分为无缓冲信道和有缓冲信道
无缓冲信道:信道是同步的,接收前没有能力保存任何值。这种类型的信道只有发送和接收同时准备好,才能进行下次信道的操作,否则会导致阻塞。
有缓冲信道:信道是异步的,是一种在被创建时就被开辟了能存储一个或者多个值的信道。这种类型并不要求发送与接收同时进行。只要缓冲区有未使用空间用于发送数据,或还包含可以接收的数据,那么其通信就会无阻塞地进行。
发送与接收默认是阻塞的(同步的)。这是什么意思?当把数据发送到信道时,程序控制会在发送数据的语句处发生阻塞,直到有其它 Go 协程从信道读取到数据,才会解除阻塞。
与此类似,当读取信道的数据时,如果没有其它的协程把数据写入到这个信道,那么读取过程就会一直阻塞着。
信道的这种特性能够帮助 Go 协程之间进行高效的通信,不需要用到其他编程语言常见的显式锁或条件变量。
信道声明
var ch chan T
我们声明了一个T类型的名称叫做ch的信道
信道的 0 值为 nil。我们需要通过内置函数 make 来创建一个信道,就像创建 map 和 slice 一样。
无缓冲信道
ch := make(chan T)
有缓冲信道
ch := make(chan T,cap)
(1)信道的创建
//内置类型channel
var a chan int
if a == nil {
a = make(chan int)
fmt.Printf("%T\n", a) //chan int
}
//自定义类型channel
var p chan person
if p == nil {
p = make(chan person) //chan main.person
fmt.Printf("%T\n", p)
}
(2)通过信道发送和接收数据
注意:发送和接收默认是阻塞的
data := <- a // 从信道 a 中读取数据并将读取的值赋值给变量 data 。
a <- data // 向信道 a 中写入数据。
package main
import (
"fmt"
"time"
)
func hello(done chan bool) {
fmt.Println("hello go routine is going to sleep")
time.Sleep(4 * time.Second)
//只有写数据后才能继续执行
done <- true
fmt.Println("hello go routine awake and going to write to done")
}
func main() {
done := make(chan bool)
go hello(done)
<-done
fmt.Println("Main received data")
}
(4)死锁
当 Go 协程给一个信道发送数据时,照理说会有其他 Go 协程来接收数据。如果没有的话,程序就会在运行时触发 panic,形成死锁。
同理,当有 Go 协程等着从一个信道接收数据时,我们期望其他的 Go 协程会向该信道写入数据,要不然程序就会触发 panic。
package main func main() {
ch := make(chan int)
ch <- 5
}
(5)单向信道与双向信道
双向信道:即通过信道既能发送数据,又能接收数据。
单向信道:信道只能发送或者接收数据。
单向信道:
package main import "fmt" func sendData(sendch chan<- int) {
sendch <- 10
} func main() {
sendch := make(chan<- int)
go sendData(sendch)
fmt.Println(<-sendch)
}
chan<- int 定义了唯送信道,因为箭头指向了 chan。我们在主函数中试图通过唯送信道接收数据,于是编译器报错:
invalid operation: <-sendch (receive from send-only type chan<- int)
一切都很顺利,只不过一个不能读取数据的唯送信道究竟有什么意义呢?
这就需要用到信道转换(Channel Conversion)了。把一个双向信道转换成唯送信道或者唯收(Receive Only)信道都是行得通的,但是反过来就不行。
package main import "fmt" func sendData(sendch chan<- int) {
sendch <- 10
}
func main() {
cha1 := make(chan int)
go sendData(cha1)
fmt.Println(<-cha1)
}
我们创建了一个双向信道 cha1。 cha1 作为参数传递给了 sendData 协程。函数 sendData 里的参数 sendch chan<- int 把 cha1 转换为一个唯送信道。
于是该信道在 sendData 协程里是一个唯送信道,而在 Go 主协程里是一个双向信道。该程序最终打印输出 10。
(6)关闭信道
数据发送方可以关闭信道,通知接收方这个信道不再有数据发送过来。
当从信道接收数据时,接收方可以多用一个变量来检查信道是否已经关闭。
v, ok := <- ch
上面的语句里,如果成功接收信道所发送的数据,那么 ok 等于 true。而如果 ok 等于 false,说明我们试图读取一个关闭的通道。
从关闭的信道读取到的值会是该信道类型的零值。例如,当信道是一个 int 类型的信道时,那么从关闭的信道读取的值将会是 0。
package main import (
"fmt"
) func producer(chnl chan int) {
for i := 0; i < 10; i++ {
chnl <- i
} close(chnl)
} func main() {
ch := make(chan int)
go producer(ch)
for {
v, ok := <-ch
if ok == false {
break
}
fmt.Println("Received ", v, ok)
}
}
在上述的程序中,producer 协程会从 0 到 9 写入信道 chn1,然后关闭该信道。
主函数有一个无限的 for 循环,使用变量 ok检查信道是否已经关闭。如果 ok 等于 false,说明信道已经关闭,于是退出 for 循环。
如果 ok 等于 true,会打印出接收到的值和 ok 的值。
(7)遍历信道
信道支持range for遍历
package main import (
"fmt"
"time"
) func producer(chnl chan int) {
defer close(chnl) //程序执行结束关闭信道
for i := 0; i < 10; i++ {
time.Sleep(300 * time.Millisecond) //一秒写一次
chnl <- i //写操作
}
}
func main() {
ch := make(chan int)
go producer(ch)
//接收ch信道中的数据,直到该信道关闭。
for v := range ch {
fmt.Println(v)
}
}
也可以自定for循环遍历信道
for {
v, ok := <-ch //读操作
fmt.Println(v, ok)
if ok == false { //当读取不到数据跳出循环
break
}
}
Go 信道Channel的更多相关文章
- RabbitMQ 信道(channel)挂掉,但连接仍然存在,同时出现错误:Received remote Channel.Close (406): PRECONDITION_FAILED - unknown delivery tag x 的问题
该问题经过一番试验,发现是消费者(consumer)程序逻辑错误导致:在消息处理的回调函数中多次ack或nack. 开启Python日志,并在回调函数中两次ack得到如下信息: F:\software ...
- rabbitmq一个连接多个信道channel
https://www.cnblogs.com/eleven24/p/10326718.html
- what is the purpose of channel coding?(信道编码的作用?)
信道.信道编码及其作用 1.信道(channel) 信道和通信电路并不等同,用来表示向某一个方向传送信息的媒体.因此一条通信线路往往包含一条发送信道和一条接收信道. 从通信的双方信息交互方式看有三个基 ...
- golang 学习 ---- channel
把一个loop放在一个goroutine里跑,我们可以使用关键字go来定义并启动一个goroutine: package main import "fmt" func loop() ...
- 论文笔记——Channel Pruning for Accelerating Very Deep Neural Networks
论文地址:https://arxiv.org/abs/1707.06168 代码地址:https://github.com/yihui-he/channel-pruning 采用方法 这篇文章主要讲诉 ...
- 深入学习RabbitMQ(四):channel的confirm模式
转自:http://m.blog.csdn.net/article/details?id=54340711 上一篇博客我们介绍了使用RabbitMQ可能会遇到的一个问题,即生产者不知道消息是否真正到达 ...
- 如何优雅的使用RabbitMQ
RabbitMQ无疑是目前最流行的消息队列之一,对各种语言环境的支持也很丰富,作为一个.NET developer有必要学习和了解这一工具.消息队列的使用场景大概有3种: 1.系统集成,分布式系统的设 ...
- 解析大型.NET ERP系统 分布式应用模式设计与实现
C/S架构的应用程序,将一些复杂的计算逻辑由客户端转移到服务器端可以改善性能,同时也为了其它方面的控制..NET Remoting在局域网内调用的性能相当不错.ERP系统中基于.NET Remotin ...
- [Java 安全]加密算法
Base64编码 算法简述 定义 Base64内容传送编码是一种以任意8位字节序列组合的描述形式,这种形式不易被人直接识别. Base64是一种很常见的编码规范,其作用是将二进制序列转换为人类可读的A ...
随机推荐
- PAT 甲级 1059 Prime Factors (25 分) ((新学)快速质因数分解,注意1=1)
1059 Prime Factors (25 分) Given any positive integer N, you are supposed to find all of its prime ...
- PAT 甲级 1036 Boys vs Girls (25 分)(简单题)
1036 Boys vs Girls (25 分) This time you are asked to tell the difference between the lowest grade ...
- 第三方框架MBProgressHUD-----实现各种提示框
程序运行显示如下 : 点击按钮实现对应的提示框: 这里只截取了其中一张图,有兴趣的可以自己运行程序,查看其他的几种提示框哟!!! 第三方框架MBProgressHUD的下载地址:https://git ...
- win10系统安装踩坑之路
1.一定要下载win10原版镜像.如果用迅雷下载一定要校验文件hash值的完整性,可以用fhash.exe校验,如果哈希值不一致,一定要重新下载镜像. 2.用软媒U盘启动制作启动U盘 3.重启后按F1 ...
- 浅析 c# Queue
1.Queue定义 System.Collections.Queue类表示对象的先进先出集合,存储在 Queue(队列) 中的对象在一端插入,从另一端移除. 2.优点 1.能对集合进行顺序处理(先进先 ...
- iOS面试题超全!
之前看了很多面试题,感觉要不是不够就是过于冗余,于是我将网上的一些面试题进行了删减和重排,现在分享给大家.(题目来源于网络,侵删) 1. Object-c的类可以多重继承么?可以实现多个接口么?Cat ...
- iOS-iphone网络编程总结
iphone网络编程总结 一:确认网络环境3G/WIFI 1. 添加源文件和framework 开发Web等网络应用程序的时候,需要确认网络环境,连接情况等信息.如果没有处理它们,是不会 ...
- 02.提交bug
写代码最烦的也就是修复bug了,虽然这个避无可避…………………… a. bug的严重级别设置 1级:影响主要流程 ->在bug 的影响下,主流程 测试无法向下进行 2级:影响核心功能 -> ...
- eclipse 解决POM文件错误:org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project.MavenProject, org.apache.maven.archiver.MavenArchiveConfiguration)
解决方案: 更新eclipse中的maven插件 1.1 Help -> Install New Software -> Add 1.2 Location中输入 http://repo1. ...
- Flutter 弹出键盘屏幕溢出问题
在使用输入框获取焦点,弹出键盘的时候,会导致屏幕溢出, 解决办法: resizeToAvoidBottomPadding: false, //输入框抵住键盘 return Scaffold( appB ...