golang学习笔记——将 channel 用作通信机制

news/2025/2/12 3:01:14/

文章目录

  • 将 channel 用作通信机制
  • Channel 语法
  • 无缓冲 channel
  • 缓冲 channels
    • channel 与 goroutine
    • 缓冲 channels 示例
    • 多路复用

将 channel 用作通信机制

golang学习笔记——将 channel 用作通信机制
golang学习笔记——并发计算斐波纳契数

Go 中的 channel 是 goroutine 之间的通信机制。 请记住 Go 的并发方法是:不是通过共享内存通信;而是通过通信共享内存。当你需要将值从一个 goroutine 发送到另一个时,可以使用通道。 让我们看看它们的工作原理,以及如何开始使用它们来编写并发 Go 程序。

Channel 语法

ch <- x // sends (or writes ) x through channel ch
x = <-ch // x receives (or reads) data sent to the channel ch
<-ch // receives data, but the result is discarded

关闭 channel

close(ch)

无缓冲 channel

使用 make() 函数创建 channel 时,会创建一个无缓冲 channel,这是默认行为。 无缓冲 channel 会阻止发送操作,直到有人准备好接收数据。

package mainimport ("fmt""net/http""time"
)func main() {start := time.Now()apis := []string{"https://mp.csdn.net/","https://dev.azure.com","https://api.somewhereintheinternet.com/","https://gitcode.net/",}ch := make(chan string)for _, api := range apis {go checkAPI(api, ch)}for i := 0; i < len(apis); i++ {fmt.Print(<-ch)}elapsed := time.Since(start)fmt.Printf("Done! It took %v seconds!\n", elapsed.Seconds())
}func checkAPI(api string, ch chan string) {_, err := http.Get(api)if err != nil {ch <- fmt.Sprintf("ERROR: %s is down!\n", api)return}ch <- fmt.Sprintf("SUCCESS: %s is up and running!\n", api)
}

缓冲 channels

下面是一个理解有缓冲 channel 的简单示例

package mainimport ("fmt"
)func send(ch chan string, message string) {ch <- message
}func main() {size := 4ch := make(chan string, size)send(ch, "one")send(ch, "two")send(ch, "three")send(ch, "four")fmt.Println("All data sent to the channel ...")for i := 0; i < size; i++ {fmt.Println(<-ch)}fmt.Println("Done!")
}

输出

All data sent to the channel ...
one
two
three
four
Done!

试着将size改为2
重新运行程序时,将看到以下错误:

fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:
main.send(...)D:/golang2023/main.go:8
main.main()D:/golang2023/main.go:16 +0x97
exit status 2

channel 与 goroutine

channel 与 goroutine 有着紧密的联系。 如果没有另一个 goroutine 从 channel 接收数据,则整个程序可能会永久处于被阻止状态。 正如你所见,这种情况确实会发生。

缓冲 channels 示例

使用之前用于检查 API 的示例,并创建大小为 10 的缓冲通道

package mainimport ("fmt""net/http""time"
)func main() {start := time.Now()apis := []string{"https://management.azure.com","https://dev.azure.com","https://mp.csdn.net/","https://outlook.office.com/","https://api.somewhereintheinternet.com/","https://gitcode.net/",}ch := make(chan string, 10)for _, api := range apis {go checkAPI(api, ch)}for i := 0; i < len(apis); i++ {fmt.Print(<-ch)}elapsed := time.Since(start)fmt.Printf("Done! It took %v seconds!\n", elapsed.Seconds())
}func checkAPI(api string, ch chan string) {_, err := http.Get(api)if err != nil {ch <- fmt.Sprintf("ERROR: %s is down!\n", api)return}ch <- fmt.Sprintf("SUCCESS: %s is up and running!\n", api)
}

多路复用

最后,让我们讨论如何使用 select 关键字与多个通道同时交互。 有时,在使用多个 channel 时,需要等待事件发生。 例如,当程序正在处理的数据中出现异常时,可以包含一些逻辑来取消操作。

select 语句的工作方式类似于 switch 语句,但它适用于 channel。 它会阻止程序的执行,直到它收到要处理的事件。 如果它收到多个事件,则会随机选择一个。

select 语句的一个重要方面是,它在处理事件后完成执行。 如果要等待更多事件发生,则可能需要使用循环。

让我们使用以下程序来看看 select 的运行情况:

package mainimport ("fmt""time"
)func process(ch chan string) {time.Sleep(3 * time.Second)ch <- "Done processing!"
}func replicate(ch chan string) {time.Sleep(1 * time.Second)ch <- "Done replicating!"
}func main() {ch1 := make(chan string)ch2 := make(chan string)go process(ch1)go replicate(ch2)for i := 0; i < 2; i++ {select {case process := <-ch1 :fmt.Println(process)case replicate := <-ch2 :fmt.Println(replicate)}}
}

输出

Done replicating!
Done processing!

请注意,replicate 函数首先完成,这就是首先在终端中看到其输出的原因。 main 函数存在一个循环,因为 select 语句在收到事件后立即结束,但我们仍在等待 process 函数完成。


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

相关文章

【LeetCode二叉树进阶题目】606. 根据二叉树创建字符串,102. 二叉树的层序遍历,107. 二叉树的层序遍历 II

二叉树进阶题目 606. 根据二叉树创建字符串解题思路及实现 102. 二叉树的层序遍历解题思路及实现 107. 二叉树的层序遍历 II解题思路及实现 606. 根据二叉树创建字符串 描述 给你二叉树的根节点 root &#xff0c;请你采用前序遍历的方式&#xff0c;将二叉树转化为一个由括号…

矩阵知识补充

正交矩阵 定义&#xff1a; 正交矩阵是一种满足 A T A E A^{T}AE ATAE的方阵 正交矩阵具有以下几个重要性质&#xff1a; A的逆等于A的转置&#xff0c;即 A − 1 A T A^{-1}A^{T} A−1AT**A的行列式的绝对值等于1&#xff0c;即 ∣ d e t ( A ) ∣ 1 |det(A)|1 ∣det(A)∣…

java--static修饰成员变量

1.static 叫静态&#xff0c;可以修饰成员变量、成员方法。 2.成员变量按照有无static修饰&#xff0c;分为两种&#xff1a; ①类变量&#xff1a;有static修饰&#xff0c;属于类&#xff0c;在计算机里只有一份&#xff0c;会被类的全部对象共享(不管那个类调用的&#x…

【开源】基于Vue和SpringBoot的创意工坊双创管理系统

项目编号&#xff1a; S 049 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S049&#xff0c;文末获取源码。} 项目编号&#xff1a;S049&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员端2.2 Web 端2.3 移动端 三、…

数字人直播系统开发要注意的陷阱

数字人做为元宇宙的底层基座&#xff0c;BAT都在跑步进场&#xff0c;目前具有前瞻性的公司都在布局数字人产业。数字人可以应用于很多业务场景&#xff0c;对今年来说&#xff0c;无疑数字人直播系统是最火的。像去年数字人直播SAAS系统定制开发的话没有个百把万是下不来的。但…

webshell之扩展免杀

由于很多企业为了防止源码泄露&#xff0c;都会使用加密扩展将代码进行加密&#xff0c;那么我们就可以就将计就计&#xff0c;将webshell也利用扩展加密&#xff0c;将特征消除&#xff0c;从而达到免杀的效果 1.php-beast 扩展地址 下载dll&#xff0c;并添加至ext中 在php…

redis运维(十五) 集合

一 集合 ① 概念 集合的元素在redis里面的世界是member集合&#xff1a; setset集合当中不允许重复的元素&#xff0c;而且set集合当中元素是没有顺序的,不存在元素下标 ② sadd、smembers、srem ③ sismember、srandmember、spop、scard spop 命令用于移除集合中的指定 …

若依框架参数验证

文章目录 一、前端触发参数校验异常1.前端页面2.前端代码 二、后端触发参数校验异常1.前端页面2.后端报错 三、后端自定义参数验证1.添加注解2.触发后端校验 一、前端触发参数校验异常 1.前端页面 输入不符合校验规则的值来触发 2.前端代码 校验规则数组 表单的元素 修…