信道(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的更多相关文章

  1. RabbitMQ 信道(channel)挂掉,但连接仍然存在,同时出现错误:Received remote Channel.Close (406): PRECONDITION_FAILED - unknown delivery tag x 的问题

    该问题经过一番试验,发现是消费者(consumer)程序逻辑错误导致:在消息处理的回调函数中多次ack或nack. 开启Python日志,并在回调函数中两次ack得到如下信息: F:\software ...

  2. rabbitmq一个连接多个信道channel

    https://www.cnblogs.com/eleven24/p/10326718.html

  3. what is the purpose of channel coding?(信道编码的作用?)

    信道.信道编码及其作用 1.信道(channel) 信道和通信电路并不等同,用来表示向某一个方向传送信息的媒体.因此一条通信线路往往包含一条发送信道和一条接收信道. 从通信的双方信息交互方式看有三个基 ...

  4. golang 学习 ---- channel

    把一个loop放在一个goroutine里跑,我们可以使用关键字go来定义并启动一个goroutine: package main import "fmt" func loop() ...

  5. 论文笔记——Channel Pruning for Accelerating Very Deep Neural Networks

    论文地址:https://arxiv.org/abs/1707.06168 代码地址:https://github.com/yihui-he/channel-pruning 采用方法 这篇文章主要讲诉 ...

  6. 深入学习RabbitMQ(四):channel的confirm模式

    转自:http://m.blog.csdn.net/article/details?id=54340711 上一篇博客我们介绍了使用RabbitMQ可能会遇到的一个问题,即生产者不知道消息是否真正到达 ...

  7. 如何优雅的使用RabbitMQ

    RabbitMQ无疑是目前最流行的消息队列之一,对各种语言环境的支持也很丰富,作为一个.NET developer有必要学习和了解这一工具.消息队列的使用场景大概有3种: 1.系统集成,分布式系统的设 ...

  8. 解析大型.NET ERP系统 分布式应用模式设计与实现

    C/S架构的应用程序,将一些复杂的计算逻辑由客户端转移到服务器端可以改善性能,同时也为了其它方面的控制..NET Remoting在局域网内调用的性能相当不错.ERP系统中基于.NET Remotin ...

  9. [Java 安全]加密算法

    Base64编码 算法简述 定义 Base64内容传送编码是一种以任意8位字节序列组合的描述形式,这种形式不易被人直接识别. Base64是一种很常见的编码规范,其作用是将二进制序列转换为人类可读的A ...

随机推荐

  1. 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 ...

  2. PAT 甲级 1036 Boys vs Girls (25 分)(简单题)

    1036 Boys vs Girls (25 分)   This time you are asked to tell the difference between the lowest grade ...

  3. 第三方框架MBProgressHUD-----实现各种提示框

    程序运行显示如下 : 点击按钮实现对应的提示框: 这里只截取了其中一张图,有兴趣的可以自己运行程序,查看其他的几种提示框哟!!! 第三方框架MBProgressHUD的下载地址:https://git ...

  4. win10系统安装踩坑之路

    1.一定要下载win10原版镜像.如果用迅雷下载一定要校验文件hash值的完整性,可以用fhash.exe校验,如果哈希值不一致,一定要重新下载镜像. 2.用软媒U盘启动制作启动U盘 3.重启后按F1 ...

  5. 浅析 c# Queue

    1.Queue定义 System.Collections.Queue类表示对象的先进先出集合,存储在 Queue(队列) 中的对象在一端插入,从另一端移除. 2.优点 1.能对集合进行顺序处理(先进先 ...

  6. iOS面试题超全!

    之前看了很多面试题,感觉要不是不够就是过于冗余,于是我将网上的一些面试题进行了删减和重排,现在分享给大家.(题目来源于网络,侵删) 1. Object-c的类可以多重继承么?可以实现多个接口么?Cat ...

  7. iOS-iphone网络编程总结

    iphone网络编程总结 一:确认网络环境3G/WIFI 1. 添加源文件和framework        开发Web等网络应用程序的时候,需要确认网络环境,连接情况等信息.如果没有处理它们,是不会 ...

  8. 02.提交bug

    写代码最烦的也就是修复bug了,虽然这个避无可避…………………… a. bug的严重级别设置 1级:影响主要流程 ->在bug 的影响下,主流程 测试无法向下进行 2级:影响核心功能 -> ...

  9. 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. ...

  10. Flutter 弹出键盘屏幕溢出问题

    在使用输入框获取焦点,弹出键盘的时候,会导致屏幕溢出, 解决办法: resizeToAvoidBottomPadding: false, //输入框抵住键盘 return Scaffold( appB ...