go语言学习笔记 — 进阶 — 并发编程(6):通道(channel)—— 使用channel收发数据
2021/10/25 11:39:36
本文主要是介绍go语言学习笔记 — 进阶 — 并发编程(6):通道(channel)—— 使用channel收发数据,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
创建channel之后,我们可以使用channel进行数据收发操作。
使用channel发送(填充)数据
使用特殊的操作符<-
,把数据通过channel发送。
- 格式
通道变量 <- 值
通道变量是上文通过make创建的通道实例;值可以是变量、常量、表达式或函数返回值,值类型必须与make(chan 数据类型)
中的数据类型一致。
- 发送数据
在使用make创建channel后,就可以使用<-
向通道发送数据。
ch := make(chan interface{}) // 创建一个空接口通道 ch <- 0 // 把整型数据放入通道 ch <- "hello" // 把字符串放入通道
- 若数据未被接收,则发送数据将持续阻塞,直到数据被接收
把数据向通道发送,如果接收方一致没有接收,那么发送操作将持续阻塞,直到数据被接收方接收。
package main func main() { ch := make(chan int) ch <- 0 } // fatal error: all goroutines are asleep - deadlock!
go会智能发现一些永远无法发送成功的语句,并提示。上面的意思是,go运行时发现所有goroutine(包括main()函数的)都处于waiting状态,即所有goroutine并没有发送或接收数据对应的通道。
使用channel接收(取出)数据
通道接收(取出)数据的特性
- 通道收发数据需要在两个不同的goroutine之间进行
通过channel发送数据,如果通道中没有另外一个goroutine接收,数据发送方goroutine会持续阻塞,直到接收方goroutine接收数据;通过channel接收数据,如果通道中没有发送方goroutine发送数据,数据接收方goroutine将持续阻塞,直到发送方goroutine发送数据为止。
- 通道一次只能接收一个元素数据
通道接收(取出)数据的格式
一共有4种通道接收数据的写法。
- 阻塞模式接收数据
把接收变量作为<-
操作符的左值。执行此语句,发送方将会持续阻塞,直至接收方接收数据并赋值给data变量。
data := <-ch
- 非阻塞模式接收数据(高CPU占用,少用)
使用非阻塞方式从通道接收数据,语句不会阻塞。
data, ok := <-ch
data表示接收数据的通道变量,在没有接收到数据时,data为通道类型的零值。ok表示是否成功从通道接收到数据。
- 接收任意数据,忽略接收的数据
阻塞模式接收数据后,忽略从通道返回的数据。
<-ch
执行此语句,将会发生数据接收阻塞,直至接收到数据,但接收到的数据会被忽略。此方式使用channel在不同goroutine间阻塞收发,实现并发同步。
package main import "fmt" func main() { ch := make(chan int) // 构建一个同步通道 go func() { // 开一个匿名函数的并发 fmt.Println("start goroutine") ch <- 0 // 匿名函数快结束时,通过channel告知main()函数的goroutine,数据发送会一直阻塞,直到main()函数的goroutine接收为止。 fmt.Println("exit goroutine") }() fmt.Println("wait goroutine") <-ch // 开启匿名函数的goroutine后,阻塞模式等待发送数据,接收任意数据后忽略 fmt.Println("all done") } /* wait goroutine start goroutine exit goroutine all done */
- 循环接收
通道数据可以使用for range
语句,进行多个元素的接收。遍历得到变量data。格式如下:
for data := range ch { 循环体 }
通道是可遍历的,遍历结果是接收到的数据,其数据类型是通道的元素类型。
package main func main() { ch := make(chan int) // 生成一个整型元素的通道 go func() { // 把匿名函数并发执行 for i := 3;i >= 0; i-- { ch <- i // 使用通道发送值i time.Sleep(time.Second) // 每次发送完暂停1秒 } }() for data := range ch { // 使用for range从通道中接收数据 fmt.Println(data) // 打印接收的变量 if data == 0 { // 当接收到0时,终止for range break // 如果发送端继续发,接收goroutine已经推出,会报panic } } } /* 3 2 1 0 */
使用goroutine和channel,实现并发打印
设计生产者与消费者模型:生产者是main函数的for循环,消费者是printer函数。整个模型使用两个goroutine:一个是main(),另一个是go printer创建的goroutine,两个goroutine通过第21行的通道进行通信。
这个通道有两个功能:
- 数据传送:第26行发送数据,第9行接收数据
- 控制指令:类似信号量的功能,同步goroutine操作。如第17行、29行、31行
package main import "fmt" // 定义打印函数 func printer(c chan int) { for { // 启动一个死循环 data := <-c // 从函数入参channel中取得整型数据 if data == 0 { break } fmt.Println(data) // 输出刚取得的整型数据 } c <- 0 // 退出循环时,通知main函数(我搞定了) } func main() { c := make(chan int) // 创建一个channel go printer(c) // 并发执行,传入channel for i := 1; i <= 10; i++ { // 使用for循环,把1~10通过通道c发送给printer函数的goroutine c <- i } c <- 0 // 发完数据后,通知并发的printer结束循环(没数据了) <-c // 阻塞式等待接收printer发送退出数据,然后main函数才能结束(搞定喊我), }
这篇关于go语言学习笔记 — 进阶 — 并发编程(6):通道(channel)—— 使用channel收发数据的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-15SendGrid 的 Go 客户端库怎么实现同时向多个邮箱发送邮件?-icode9专业技术文章分享
- 2024-11-15SendGrid 的 Go 客户端库怎么设置header 和 标签tag 呢?-icode9专业技术文章分享
- 2024-11-12Cargo deny安装指路
- 2024-11-02MongoDB项目实战:从入门到初级应用
- 2024-11-01随时随地一键转录,Google Cloud 新模型 Chirp 2 让语音识别更上一层楼
- 2024-10-25Google Cloud动手实验详解:如何在Cloud Run上开发无服务器应用
- 2024-10-24AI ?先驱齐聚 BAAI 2024,发布大规模语言、多模态、具身、生物计算以及 FlagOpen 2.0 等 AI 模型创新成果。
- 2024-10-20goland工具下,如修改一个项目的标准库SDK的版本-icode9专业技术文章分享
- 2024-10-17Go学习:初学者的简单教程
- 2024-10-17Go学习:新手入门完全指南