Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理

news/2024/10/23 9:24:33/

目录

管道(channel)

无缓冲管道

有缓冲管道

需要注意

goroutine与channel实现并发

单向管道

定义单向管道

将双向管道转换为单向管道

单向管道作为函数参数

单向管道的代码示例

select多路复用

案例演示

goroutine panic处理

案例演示


管道(channel)

管道(channel)是 Go 语言中实现并发的一种方式,它可以在多个 goroutine 之间进行通信和数据交换。管道可以看做是一个队列,通过它可以进行先进先出的数据传输,支持并发的读和写。

Go 语言中使用 make 函数来创建一个管道,它的语法如下:

ch := make(chan 数据类型)

其中,数据类型可以是任意的 Go 语言数据类型,例如 int、string 等。创建了一个管道之后,我们就可以在多个 goroutine 之间进行数据传输了。

无缓冲管道

无缓冲管道是指在创建管道时没有指定容量,也就是说,它只能存储一个元素,当一个 goroutine 尝试向管道发送数据时,它会阻塞直到另一个 goroutine 从管道中读取数据。同样的,当一个 goroutine 尝试从管道中读取数据时,它也会阻塞直到另一个 goroutine 向管道中发送数据。

示例代码:

package mainimport ("fmt"
)func main() {ch := make(chan int)go func() {ch <- 10}()fmt.Println(<-ch)
}

创建了一个无缓冲管道 ch,并在一个 goroutine 中向管道中发送了一个整数 10。在主 goroutine 中,我们通过 <-ch 从管道中读取数据并打印出来。

有缓冲管道

有缓冲管道是指在创建管道时指定了容量,这时候它可以存储多个元素,但是当管道已满时,尝试向管道发送数据的 goroutine 会被阻塞,直到另一个 goroutine 从管道中读取数据以腾出空间。同样的,当管道为空时,尝试从管道中读取数据的 goroutine 也会被阻塞,直到另一个 goroutine 向管道中发送数据。

示例代码:

package mainimport ("fmt"
)func main() {ch := make(chan int, 2)ch <- 10ch <- 20fmt.Println(<-ch)fmt.Println(<-ch)
}

创建了一个容量为 2 的有缓冲管道 ch,并在主 goroutine 中向管道中依次发送了整数 1020。接着,我们依次从管道中读取数据并打印出来。

需要注意

1.管道是有缓冲的,可以通过指定缓冲区大小来控制数据在管道中的流动。如果缓冲区已满,写入操作将会阻塞直到缓冲区有空间;如果缓冲区为空,读取操作将会阻塞直到有数据写入。

2.管道的写入和读取操作都是阻塞的,直到操作完成才会返回。如果需要非阻塞的读写操作,可以使用select语句进行多路复用。

3.管道可以被关闭,一旦管道被关闭,读取操作将不再阻塞,返回一个零值和一个标识管道已关闭的错误;写入操作将会抛出 panic。为了避免 panic,可以在写入操作之前先检查管道是否已关闭。

4.管道可以用作信号量或同步器,例如使用一个无缓冲的管道实现多个 goroutine 之间的同步。

goroutine与channel实现并发

下面是一个协程与管道实现并发的代码示例,其中使用了两个管道,一个用于发送整数数据,另一个用于接收处理后的数据:

package mainimport ("fmt"
)func produce(out chan<- int) {for i := 0; i < 5; i++ {out <- i}close(out)
}func consume(in <-chan int, out chan<- int) {for v := range in {out <- v * v}close(out)
}func main() {ch1 := make(chan int)ch2 := make(chan int)go produce(ch1)go consume(ch1, ch2)for v := range ch2 {fmt.Println(v)}
}

代码分析:

1.使用 make 函数创建了两个整数类型的管道 ch1ch2

2.使用 go 关键字分别启动了函数 produceconsume 的协程,其中函数 produce 向管道 ch1 中发送了整数数据,函数 consume 从管道 ch1 中接收数据进行处理,将处理结果发送到管道 ch2 中。

3.在主协程中,使用 range 关键字从管道 ch2 中循环接收处理结果,并将接收到的数据打印出来。

单向管道

在 Go 语言中,有的时候我们会将管道作为参数在多个任务函数间传递,很多时候我们在不同的任务函数中使用管道都会对其进行限制,比如限制管道在函数中只能发送或者只能接收。

定义单向管道

定义一个单向管道可以使用 channel 类型加上箭头运算符(<-)指定读写方向。

例如,定义一个只能写入字符串的单向管道可以使用以下语句:

var ch chan<- string

定义一个只能读出字符串的单向管道可以使用以下语句:

var ch <-chan string   

将双向管道转换为单向管道

双向管道可以转换为只读或只写的单向管道,例如,将一个双向管道转换为只读的单向管道可以使用以下语句:

var ch chan string
var chRead <-chan string = ch

将一个双向管道转换为只写的单向管道可以使用以下语句:

var ch chan string
var chWrite chan<- string = ch

单向管道作为函数参数

单向管道可以作为函数参数来限制管道的读写方向。例如,以下函数接受只读的单向管道作为参数:

func readData(ch <-chan string) {// ...
}

以下函数接受只写的单向管道作为参数:

func writeData(ch chan<- string) {// ...
}

单向管道的代码示例

以下是一个使用单向管道的代码示例,该示例将一个双向管道转换为只读和只写的单向管道,并将这些单向管道作为函数参数传递:

package mainimport "fmt"func readData(ch <-chan int) {for i := range ch {fmt.Println("Read data:", i)}
}func writeData(ch chan<- int) {for i := 0; i < 5; i++ {ch <- i}close(ch)
}func main() {ch := make(chan int)chRead := (<-chan int)(ch)chWrite := (chan<- int)(ch)go readData(chRead)go writeData(chWrite)select {}
}

在上面的代码示例中,定义了一个双向管道 ch,然后将它转换为只读的单向管道 chRead 和只写的单向管道 chWrite,并分别将它们作为 readData 和 writeData 函数的参数传递。在 main 函数中,将 readData 和 writeData 函数放入不同的 goroutine 中运行,以便它们可以并发地读取和写入数据。最后使用 select {} 让主程序保持运行,以便 goroutine 可以继续运行。

select多路复用

在Go语言中,select语句可以用于多路复用I/O操作,其语法结构类似于switch语句。它可以同时监视多个管道的读写操作,并在其中一个通道满足读写条件时执行相应的操作。

select语句的语法如下:

select {
case <-ch1:// 处理从 ch1 读取到的数据
case data := <-ch2:// 处理从 ch2 读取到的数据
case ch3 <- data:// 向 ch3 写入数据
default:// 如果没有任何 case 语句满足条件,则执行 default 语句
}

select语句中,每个case分支必须是一个通道操作,要么是从通道中读取数据,要么是向通道中写入数据。其中,default分支是可选的,表示如果没有任何case语句满足条件,则执行default语句。

案例演示

package mainimport ("fmt""time"
)func main() {ch1 := make(chan int)ch2 := make(chan int)go func() {for i := 1; i <= 5; i++ {ch1 <- itime.Sleep(time.Second)}}()go func() {for i := 1; i <= 5; i++ {ch2 <- i * itime.Sleep(500 * time.Millisecond)}}()for i := 0; i < 10; i++ {select {case data := <-ch1:fmt.Println("Received from ch1:", data)case data := <-ch2:fmt.Println("Received from ch2:", data)}}
}

在这个示例中,我们创建了两个通道ch1ch2,并分别向其中写入一些数据。在主函数中,我们使用select语句监听这两个通道,并在其中一个通道中有数据时输出该数据。由于ch1的写入间隔为1秒,而ch2的写入间隔为500毫秒,因此我们可以看到输出的数据是交替出现的。

goroutine panic处理

panic是Go语言中的一种异常处理机制,它的出现是为了让程序在遇到某些不可控制的情况时,能够快速反应,而不是无限期的等待。panic的用法有两种:一种是在程序中显式地调用panic函数,用于处理特定的异常情况;另一种是在程序运行过程中,由于某些不可控制的原因,程序自动抛出panic异常。

案例演示

// 函数
func sayHello() {for i := 0; i < 10; i++ {fmt.Println("hello,world")}
}// 问题函数
func test() {//这里使用defer + recoverdefer func() { //匿名自执行函数if err := recover(); err != nil {fmt.Println("test() 发生错误", err)}}()//定义一个mapvar myMap map[int]stringmyMap[0] = "hello"
}func main() {//当两个协程中一个出现问题时,另一个也不会进行操作,可以使用异常处理避免go sayHello()go test()//防止主进程退出这里利用time.Sleeptime.Sleep(time.Second)
}

输出结果:


http://www.ppmy.cn/news/102716.html

相关文章

Musl libc 库成功适配到 openEuler Embedded,推动欧拉嵌入式生态发展

近期&#xff0c;RISC-V SIG 在欧拉嵌入式操作系统上成功实现了 musl libc 的适配&#xff0c;完成了使用 musl libc 库替换 glibc 库构建镜像的工作。目前&#xff0c;以 musl libc 为基础库编译的镜像已在 Raspberry Pi4 开发板上可用&#xff0c;这一成果推动了 openEuler E…

2023音视频开发程序员未来10年路线选择

2023音视频开发程序员未来10年路线选择&#xff1a; 音视频领域&#xff0c;其实你可以分三个部分来看&#xff0c; 第一是音视频本身&#xff0c;第二是网络通讯&#xff0c;第三是图像处理。 音视频本身涉及到音视频视频编解码啊&#xff0c;各种视频容器啊等等协议规范。 网…

“外行转网工,我只用了三个月”

大家好&#xff0c;我是老杨。 在这行发展了这么多年&#xff0c;经常会有人来问我&#xff0c;网工该怎么提升自己&#xff0c;又或是怎么入行。 其实这事儿不难想&#xff0c;技术工种最需要做的是什么&#xff0c;自然是提升技术。 而技术提升&#xff0c;途径也只有学习…

企业电子招标采购系统源码Spring Cloud + Spring Boot + MybatisPlus + 前后端分离 + 二次开发

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及…

拼多多获取整站实时商品详情数据|商品标题|商品链接,数据采集,数据分析提取教程

拼多多是一个基于社交电商的购物平台&#xff0c;它通过通过价格和优惠吸引大量用户&#xff0c;使用户形成消费场景和消费共同体&#xff0c;最终实现规模效应。在拼多多运营中&#xff0c;API接口起到了重要的作用&#xff0c;它可以实现不同系统之间的信息共享和数据传递&am…

机器学习常识 7: 决策树

摘要: 决策树是一种与人类思维一致, 可解释的模型. 1. 决策树的结构 人类的很多知识以决策规则的形式存储: 如果今天是阴天 (outlook overcast), 就去打球.如果今天出太阳 (outlook sunny) 而且湿度不高于 70% (humidity ≤ \le ≤ 70), 就去打球.如果今天出太阳 (outloo…

git commit后回退方法

使用导入CSV的时候需要创建一个public/files/文件夹&#xff0c;进行测试数据的是时候&#xff0c;测试文件放在文件夹里一起提交了&#xff0c; git commit -m public/upload_files/ 增加了目录&#xff0c;用来导入文件使用[main 9a695f2] public/upload_files/ 增加了目录&…

使用Nextcloud搭建私人云盘,并内网穿透实现公网远程访问

文章目录 摘要视频教程1. 环境搭建2. 测试局域网访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问 4 配置固定http公网地址4.1 保留一个二级子域名4.1 配置固定二级子域名4.3 测试访问公网固定二级子域名 转载自cpolar极点云的文章&#xff1a;使用Nextcl…