GO通道:无缓冲通道与缓冲通道
转载请注明出处:
1.通道定义
在多个协程之间进行通信和管理,可以使用 Go 语言提供的通道(Channel)类型。通道是一种特殊的数据结构,可以在协程之间进行传递数据,从而实现协程之间的通信和同步。多个协程可以同时读写同一个通道,通过通道来进行数据的传递和共享。
通道遵循先入先出(First In First Out)的原则,保证收发数据的顺序。通道是一个特殊的数据类型,在使用之前必须定义和创建通道变量,定义通道的语法如下:
var name chan type
语法格式说明如下:
1)var是Go语言关键字,用于定义变量。
2)name是通道变量名称,可自行命名。
3)chan是Go语言关键字,将变量定义为通道类型。
4)type是通道存放的数据类型。
通道定义之后,还需要使用关键字make创建通道,通道的创建语法如下:
name := make(chan type, num)
语法格式说明如下:
1)name是通道变量名称,可自行命名。
2)make是Go语言关键字,用于创建通道。
3)chan type的chan是Go语言关键字,type是通道能存放的数据类型。
4)num是通道存放数据的数量上限。
在实际编程中,我们直接使用关键字make创建通道即可使用,这样能省去定义通道的过程,示例代码如下:
// 定义和创建通道
var ch chan string
ch = make(chan string)
// 直接创建通道,无须定义
ch := make(chan string)
通道创建之后,使用通道完成写入和读取数据操作。在通道里面写入和读取数据需要由<-操作符实现,使用说明如下:
// 构建通道
ch := make(chan string)
// 往通道写入数据
ch <- "Hello"
// 从通道获取数据,赋予变量s
s := <- ch
2.无缓冲通道
无缓冲通道是 Go 语言中一种常见的通道类型,也称为同步通道或阻塞通道。无缓冲通道的特点是在发送和接收数据时,必须有另外一个协程同时进行相反的操作,否则会阻塞当前协程。 具体来说,无缓冲通道的特点如下:
发送和接收操作是同步的,即发送操作必须等待接收操作完成后才能继续执行,接收操作也必须等待发送操作完成后才能继续执行。
无缓冲通道的容量为 0,即只有在发送和接收操作同时进行时才能传递数据,否则会阻塞当前协程。
无缓冲通道的数据传递是按照先进先出的顺序进行的,即发送的数据会按照发送的顺序被接收。 无缓冲通道可以用于协程之间的同步和通信,例如在生产者和消费者模式中,可以使用无缓冲通道来传递数据,从而保证生产者和消费者之间的同步和互斥。同时,无缓冲通道的使用也可以避免数据竞争问题,从而提高程序的安全性和可靠性。
通道是通过关键字make创建的,在创建过程中,如果没有设置参数num,则视为创建无缓冲通道。无缓冲通道(Unbuffered Channel)是指在获取数据之前没有能力保存数据的通道,这种类型的通道要求两个Goroutine同时处于执行状态才能完成写入和获取操作。
如果两个Goroutine没有同时准备,某一个Goroutine执行写入或获取操作将会处于阻塞等待状态,另一个Goroutine无法执行写入或获取操作,程序将会提示异常,这种类型的通道执行写入和获取的交互行为是同步,任意一个操作都无法离开另一个操作单独存在。
当我们使用无缓冲通道的时候,必须注意通道变量的操作,确保程序中有两个或两个以上的Goroutine同时执行通道的读写操作,读写操作必须是一读一写,不能只读不写或只写不读,示例如下:
// 只写入数据,不读取
ch := make(chan string)
ch <- "Tom"
fmt.Println("wait goroutine")
// 只读取数据,不写入
ch := make(chan string)
<- ch
fmt.Println("wait goroutine")
通道数据只写入不读取或者只读取不写入都会提示fatal error: all goroutines are asleep–deadlock异常,如果需要实现通道数据获取超时检测,可以使用关键字select实现。
如果程序中仅有一个Goroutine,使用通道读写数据也会导致异常,比如在主函数main()中对通道写入数据,再读取通道数据,示例如下:
package main
import (
"fmt"
)
func main() {
// 构建通道
ch := make(chan string)
// 写入通道数据
ch <- "Tom"
// 读取通道数据
<-ch
fmt.Println("wait goroutine")
}
如果在发送和接收数据时出现异常,则会引发程序异常。例如,如果我们在发送数据之前关闭通道,则会引发一个运行时异常。为了避免这种情况的发生,我们可以使用 defer
语句在函数退出之前关闭通道。例如:
func main() {
ch := make(chan int)
defer close(ch) // 使用 defer 关闭通道
go func() {
fmt.Println("开始发送消息...")
ch <- 1
fmt.Println("消息发送完成。")
}()
fmt.Println("开始接收消息...")
msg := <-ch
fmt.Printf("接收到的消息是:%d\n", msg)
fmt.Println("消息接收完成。")
}
3.带缓冲通道
带缓冲通道(Buffered Channel)是在被获取前能存储一个或者多个数据的通道,这种类型的通道并不强制要求Goroutine之间必须同时完成写入和获取。当通道中没有数据的时候,获取动作才会阻塞;当通道没有可用缓冲区存储数据的时候,写入动作才会阻塞。
在无缓冲通道的基础上,只要为通道增加一个有限大小的存储空间就能形成带缓冲通道。带缓冲通道在写入时无须等待获取即可再次执行下一轮写入,并且不会发生阻塞,只有当存储空间满了才会发生阻塞。同理,如果带缓冲通道中有数据,获取时将不会发生阻塞,直到通道中没有数据可读时,通道才会阻塞。
从通道的定义角度分析,带缓冲和无缓冲通道的区别在于参数num。创建通道的时候,如果没有设置参数num,则默认参数值为0,通道为无缓冲通道,所以写入和获取数据必须同时进行才不会因阻塞而异常;如果参数num大于0,则写入和获取数据无须同步执行,因为通道有足够的空间存放数据。
由于带缓冲通道没有读写同步限制,我们可以在同一个Goroutine中执行多次写入和获取操作,具体示例如下:
package main
import "fmt"
func main() {
// 创建一个3个元素缓冲大小的整型通道
ch := make(chan int, 3)
// 查看当前通道的大小
fmt.Println(len(ch))
// 发送3个整型元素到通道
for i := 0; i < 3; i++ {
ch <- i
}
// 查看当前通道的大小
fmt.Println(len(ch))
for i := 0; i < 3; i++ {
fmt.Println(<-ch)
}
// 查看当前通道的大小
fmt.Println(len(ch))
// 查看当前通道的容量
fmt.Println(cap(ch))
}
上述代码的说明如下:
1)通过for执行了3次循环,每次循环将变量i写入通道,然后通过3次循环从通道获取数据并输出。
2)通道写入和读取数据的时候,使用len()函数获取通道已有的数据量,判断当前通道存储的数据量是否达到上限,这样可以防止程序在运行时提示异常。
3)使用cap()函数能获取通道的容量大小,即获取创建通道make()的参数num的大小。带缓冲通道在很多特性上和无缓冲通道类似,无缓冲通道可以看作长度为0的带缓冲通道。
根据这个特性,带缓冲通道在下列情况下会发生阻塞:
1)带缓冲通道的存储数据达到上限时,再次写入数据将发生阻塞而导致异常。
2)带缓冲通道没有存储数据时,获取数据将发生阻塞而导致异常。
Go语言为什么对通道要限制长度?因为多个Goroutine之间使用通道必然存在写入和获取操作,这种模式类型的典型例子为生产者消费者模式。如果不限制通道长度,当写入数据速度大于获取速度,内存将不断膨胀直到应用崩溃。因此,限制通道的长度有利于约束数据生产速度,生产数据量必须在数据消费速度+通道长度的范围内,这样才能正常地处理数据。
GO通道:无缓冲通道与缓冲通道的更多相关文章
- Linux系统编程之IO_缓冲和非缓冲
下面是一段类似日志记录的代码,已获取通讯的报文内容和当时的环境参数内容,就是创建一个文件,使用标准IO的fopen.fprintf进行输出记录.但是在调试中,刚开始我就傻眼了,文件创建成功了,但是实时 ...
- 关于hibernate一级缓冲和二级缓冲
关于一级缓冲和二级缓冲的内容,在面试的时候被问起来了,回答的不是很满意,所以有专门找了些有关这方面的文章加以理解 出自:http://blog.csdn.net/zdp072/article/deta ...
- opengl 单缓冲与双缓冲
1.说明 GLUT_SINGLE 指定单缓存窗口 GLUT_DOUBLE 指定双缓存窗口 应用程序使用单缓冲绘图时可能会存在图像闪烁的问题. 这是因为生成的图像不是一下子被绘制出来的,而是按照从左 ...
- 无缓冲和带缓冲channel的区别
常规定义的channel都是默认不带缓冲的,如下代码所示 package main import ( "fmt" ) func main() { c := make(chan in ...
- (19)IO流之字符流FileReader和FileWriter,缓冲字符流---缓冲输入字符流BufferedReader和缓冲输出字符流BufferedWriter
字符流,读取的文件是字符的时候,有两个基类一个是Reader,一个是Writer这有点拟人的感觉,人直接看懂的是文字 字符流 字节流:读取的是文件中的二进制字节流并不会帮你转换成看的懂得字符 字符流: ...
- Java基础 FileReader-FileWriter / 缓冲字符输入输出流 / 缓冲字节输入输出流 三种方式 进行文本文件的复制
易错的地方: /** 出错的地方: * 1.缓冲流儿输出时,务必:flush();不然可能输出不尽! * 2. bw缓冲字符输出流,记得这里! bw.write(b,0,len); * 3.字符流不能 ...
- 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_07 缓冲流_4_缓冲流的效率测试_复制文件
把之前文件复制的代码复制到这里 一个字节一个字节的读取,复制文件 byte数组的形式 缓冲流测试 数组缓冲
- 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_07 缓冲流_1_缓冲流的原理
一个字节一个字节的读取,先读取到a,a给到os操作系统.os再给JVM,.jVM再把a给java程序 读完a再读取b.这样一层层的返回,效率低下 一次读取,缓冲区数组返回来.
- Go语言的通道(1)-无缓冲通道
前言: 上文中我们采用了[原子函数]已经[共享锁]两种方式分别对多个goroutine进行了同步,但是在go语言中提供了另一种更好的方式,那就是使用通道(Channel). 一.通道是什么? 其实无论 ...
- [GO]无缓冲通道(unbuffered channel)
无缓冲通道(unbuffered channel)是指在接收前没有能力保存任何值的通道,在之前的例子中使用的都是无缓冲通道,需要注意的是,对于无缓冲通道而言,不管是往通道里写数据还是从通道里读数据,都 ...
随机推荐
- 系统评价——理想点TOPSIS法的R语言实现(五)
TOPSIS 法是一种常用的综合评价方法,能充分利用原始数据的信息,其结果能精确地反映各评价方案之间的差距.TOPSIS全称Technique for Order Preference by Simi ...
- kubernetes核心实战(二)---Pod+ReplicaSet
3.pod Pod 是可以在 Kubernetes 中创建和管理的.最小的可部署的计算单元. Pod (就像在鲸鱼荚或者豌豆荚中)是一组(一个或多个) 容器:这些容器共享存储.网络.以及怎样运行这些容 ...
- 【CTF】关于 .init .fini .init_array .fini_array 日志 2019.7.16 pwn
查找资料的高效性 retn 返回到栈顶地址 关于 .init .fini .init_array .fini_array 其中存放着的是在main函数执行前执行的代码,由__libc_start_ma ...
- flask基本项目结构
|-- myproject |-- config.py |-- flask-env.yml |-- manage.py |-- app | |-- models.py | |-- __init__.p ...
- pysimplegui之窗口大小,位子,主题等属性修改
重点 1finalize()或Window参数finalize=True 调用以强制窗口通过初始化的最后阶段.这将导致 tkinter 资源被分配,以便它们可以被修改.这也会导致您的窗口出现.如果您不 ...
- [AIGC]GPT模型概述
2 Open AI: ChatGPT 2.0 ChatGPT 官网 https://openai.com/ https://platform.openai.com/ 原 : https://beta. ...
- Oracle安装及各种问题
--hsql 1:jdk 本机位置:E:\Program Files\Java\jdk1.7.0_80\ 安装教程:复制然后配置环境变量 (1)新建->变量名"JAVA_HOME&qu ...
- Mysql简明语法
show databases ; use mybatis; show tables ; select * from user; select * from user where id=1; updat ...
- day33:进程锁&事件&进程队列&进程间共享数据
目录 1.锁:Lock 2.信号量:Semaphone 3.事件:Event 4.进程队列:Queue 5.生产者和消费者模型 6.JoinableQueue 7.Manager:进程之间共享数据 锁 ...
- Zabbix_sender基础命令浅析
zabbix_sender是Zabbix监控系统中用于向Zabbix服务器发送数据的命令行工具.以下是zabbix_sender基础命令教学: 语法: zabbix_sender -z <ser ...