单向管道
在 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 main
import "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 main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
for i := 1; i <= 5; i++ {
ch1 <- i
time.Sleep(time.Second)
}
}()
go func() {
for i := 1; i <= 5; i++ {
ch2 <- i * i
time.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)
}
}
}
在这个示例中,我们创建了两个通道ch1和ch2,并分别向其中写入一些数据。在主函数中,我们使用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 + recover
defer func() { //匿名自执行函数
if err := recover(); err != nil {
fmt.Println("test() 发生错误", err)
}
}()
//定义一个map
var myMap map[int]string
myMap[0] = "hello"
}
func main() {
//当两个协程中一个出现问题时,另一个也不会进行操作,可以使用异常处理避免
go sayHello()
go test()
//防止主进程退出这里利用time.Sleep
time.Sleep(time.Second)
}
输出结果: