转载请注明出处:

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 语言中一种常见的通道类型,也称为同步通道或阻塞通道。无缓冲通道的特点是在发送和接收数据时,必须有另外一个协程同时进行相反的操作,否则会阻塞当前协程。 具体来说,无缓冲通道的特点如下:

  1. 发送和接收操作是同步的,即发送操作必须等待接收操作完成后才能继续执行,接收操作也必须等待发送操作完成后才能继续执行。

  2. 无缓冲通道的容量为 0,即只有在发送和接收操作同时进行时才能传递数据,否则会阻塞当前协程。

  3. 无缓冲通道的数据传递是按照先进先出的顺序进行的,即发送的数据会按照发送的顺序被接收。 无缓冲通道可以用于协程之间的同步和通信,例如在生产者和消费者模式中,可以使用无缓冲通道来传递数据,从而保证生产者和消费者之间的同步和互斥。同时,无缓冲通道的使用也可以避免数据竞争问题,从而提高程序的安全性和可靠性。

  通道是通过关键字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通道:无缓冲通道与缓冲通道的更多相关文章

  1. Linux系统编程之IO_缓冲和非缓冲

    下面是一段类似日志记录的代码,已获取通讯的报文内容和当时的环境参数内容,就是创建一个文件,使用标准IO的fopen.fprintf进行输出记录.但是在调试中,刚开始我就傻眼了,文件创建成功了,但是实时 ...

  2. 关于hibernate一级缓冲和二级缓冲

    关于一级缓冲和二级缓冲的内容,在面试的时候被问起来了,回答的不是很满意,所以有专门找了些有关这方面的文章加以理解 出自:http://blog.csdn.net/zdp072/article/deta ...

  3. opengl 单缓冲与双缓冲

    1.说明 GLUT_SINGLE  指定单缓存窗口 GLUT_DOUBLE  指定双缓存窗口 应用程序使用单缓冲绘图时可能会存在图像闪烁的问题. 这是因为生成的图像不是一下子被绘制出来的,而是按照从左 ...

  4. 无缓冲和带缓冲channel的区别

    常规定义的channel都是默认不带缓冲的,如下代码所示 package main import ( "fmt" ) func main() { c := make(chan in ...

  5. (19)IO流之字符流FileReader和FileWriter,缓冲字符流---缓冲输入字符流BufferedReader和缓冲输出字符流BufferedWriter

    字符流,读取的文件是字符的时候,有两个基类一个是Reader,一个是Writer这有点拟人的感觉,人直接看懂的是文字 字符流 字节流:读取的是文件中的二进制字节流并不会帮你转换成看的懂得字符 字符流: ...

  6. Java基础 FileReader-FileWriter / 缓冲字符输入输出流 / 缓冲字节输入输出流 三种方式 进行文本文件的复制

    易错的地方: /** 出错的地方: * 1.缓冲流儿输出时,务必:flush();不然可能输出不尽! * 2. bw缓冲字符输出流,记得这里! bw.write(b,0,len); * 3.字符流不能 ...

  7. 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_07 缓冲流_4_缓冲流的效率测试_复制文件

    把之前文件复制的代码复制到这里 一个字节一个字节的读取,复制文件 byte数组的形式 缓冲流测试 数组缓冲

  8. 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_07 缓冲流_1_缓冲流的原理

    一个字节一个字节的读取,先读取到a,a给到os操作系统.os再给JVM,.jVM再把a给java程序 读完a再读取b.这样一层层的返回,效率低下 一次读取,缓冲区数组返回来.

  9. Go语言的通道(1)-无缓冲通道

    前言: 上文中我们采用了[原子函数]已经[共享锁]两种方式分别对多个goroutine进行了同步,但是在go语言中提供了另一种更好的方式,那就是使用通道(Channel). 一.通道是什么? 其实无论 ...

  10. [GO]无缓冲通道(unbuffered channel)

    无缓冲通道(unbuffered channel)是指在接收前没有能力保存任何值的通道,在之前的例子中使用的都是无缓冲通道,需要注意的是,对于无缓冲通道而言,不管是往通道里写数据还是从通道里读数据,都 ...

随机推荐

  1. Java泛型: 主要知识点总结

    Java泛型:主要知识点总结 1 泛型的好处 解决元素存储的安全性问题. 解决获取数据元素时,需要类型强制转换的问题. Java泛型可以保证如果程序在编译时没有发出警告,运行.时就不会产生ClassC ...

  2. 开发轻量级REST API样板 基于Node.js、MongoDB 通过Mongoose驱动

    ZY.Node.Mongodb https://gitee.com/Z568_568/node.mongodb.git https://github.com/ZHYI-source/ZY.Node.M ...

  3. mysql数据类型精讲

    1.MySQL中的数据类型 常见数据类型的属性,如下: 2.整数类型2.1 类型介绍整数类型一共有 5 种,包括 TINYINT.SMALLINT.MEDIUMINT.INT(INTEGER)和 BI ...

  4. 【Voyage】GDOI 2023 旅游记 || ECHO.

    \(\color{#FFFFFF}{那是什么样的旅途呢}\) \(\color{#FFFFFF}{真的会害怕很多东西呢.想想害怕的其实不止这样一件事,便产生了"其实都一样没关系的,都应该踏过 ...

  5. windows安装telnet命令

    1.打开控制面板 2.点击程序和功能 3.点击打开或关闭windows功能 4.等候一分钟左右 5.勾选telnet选项 6.测试telnet 7.如果通的话,弹出telnet窗口,按住"C ...

  6. JQuery点击复制文本框内容的方法插件

    [导读] 文章介绍了两种常用的点击复制文本框内容方法,一种是but IE only,同样的这个也是我们经常使用的.优点是体积小,仅有十来行代码,但缺点也很明显,只支持IE及以IE为内核的浏览器,另一种 ...

  7. CI框架内置分页代码

    Controller 控制器代码 <?php defined('BASEPATH') OR exit('No direct script access allowed'); class Welc ...

  8. 【Azure Developer】使用 Microsoft Graph API 获取 AAD User 操作示例

    问题描述 查看官方文档" Get a user " , 产生了一个操作示例的想法,在中国区Azure环境中,演示如何获取AAD User信息. 问题解答 使用Microsoft G ...

  9. 搞懂Python正则表达式,这一篇就够了

    本文代码基于Python3.11解释器,除了第一次示例,代码将省略 import re 这个语句 所有示例代码均可以在我的github仓库中的 code.py文件内查看 [我的仓库](PythonLe ...

  10. 如何通过C#/VB.NET 代码调整PDF文档的页边距

    PDF边距是页面主要内容区域和页面边缘之间的距离.与Word页边距不同,PDF文档的页边距很难更改.因为Adobe没有提供操作页边距的直接方法.但是,您可以通过缩放页面内容来改变页边距.本文将介绍如何 ...