管道是阻塞的,管道的写法,管道内容读取,和写入
go
package main
import "fmt"
func main() {
// 管道声明 chan是关键字 int是类型
// var chan1 chan int
chan1 := make(chan int)
fmt.Println(chan1)
// 无缓冲通道,不会存储数据,会直接流入管道
// 它的容量是 0,不能存储任何数据
// 数据并不会在 channel 中做任何停留。这也意味着,无缓冲 channel 的发送和接收操作是同时进行的,它也可以称为同步 channel
c := make(chan int)
go func() {
defer fmt.Println("goroutine over")
fmt.Println("goroutine正在运行...")
c <- 666 // 流入管道
}()
// 管道的读取是会被阻塞的
// 接收:receive
num, ok := <-c
if ok {
println("the chan is ok and num is ", num)
}
fmt.Println("main goroutine over")
}
带容量的chain,如果满了,那么发送的时候会阻塞,如果空了,那么接受的时候会阻塞
go
package chandemo
import (
"fmt"
"time"
)
func bufferChannel() {
// 带容量的chan
c := make(chan int, 3)
// len是实际的,cap是客观的容量
fmt.Println("len(c) = , cap(c) = ", len(c), cap(c))
go func() {
defer fmt.Println("子go结束")
// 轮询向管道发送数据
// 如果队列已满,则阻塞等待,直到另一个 goroutine 执行,接收操作释放队列的空间
for i := 0; i < 3; i++ {
c <- i
fmt.Println("子go程正在运行,发送的元素=i", i, " len(C) = ", len(c), "cap(c)", cap(c))
}
}()
// 保证先发送完成
time.Sleep(2 * time.Second)
// 循环接收数据
// 接收操作是从队列的头部获取元素并把它从队列中删除,如果队列为空,则阻塞等待,直到另一个 goroutine 执行,发送操作插入新的元素
for i := 0; i < 3; i++ {
num := <-c
fmt.Println("num of i is ", num)
}
fmt.Println("main 结束")
}
单向管道,
-
只能由发送方关闭管道:
由发送方负责关闭管道,接收方不能关闭管道。
如果接收方尝试关闭管道,会引发运行时错误。
-
重复关闭会导致 panic:
如果对同一个管道调用多次 close,会导致 panic。
因此,关闭操作应确保在逻辑上只调用一次。
-
关闭后的管道可以
继续接收数据
:但不能再向关闭的管道发送数据,否则会引发 panic。
go
package chandemo
import "fmt"
// onlySend
func counter(out chan<- int) {
for i := 0; i < 100; i++ {
out <- i
}
// 发送完成后,关闭管道
// 关闭管道可以向接收方表明,管道中不会再有新的数据。
close(out)
}
// out是只能发送,in是只能接受
func squarer(out chan<- int, in <-chan int) {
for i := range in {
out <- i * i
}
close(out)
}
// 只能接收数据的管道
// in 为单向接收管道,用于从上游接收数据并打印
func printer(in <-chan int) {
// 当管道关闭且数据读取完毕时,会自动退出循环
for val := range in {
fmt.Printf("收到数据: %d\n", val)
}
}
func testSingleChan() {
ch1 := make(chan int)
ch2 := make(chan int)
// ch1 -> ch2 -> printer
// 启动counter,将数据发送给ch1
go counter(ch1)
// 启动squarer,将ch1的数据平方后发送给ch2
go squarer(ch2, ch1)
// 启动printer,从ch2接收数据并打印
printer(ch2)
}
// 管道避免死锁
func main() {
ch := make(chan int)
go func() {
ch <- 42 // 发送数据
close(ch) // 关闭管道
}()
val, ok := <-ch
fmt.Println(val, ok) // 42 true
// 检查管道是否关闭
val, ok = <-ch
fmt.Println(val, ok) // 0 false (管道已关闭,没有数据)
}
select + chan实现多路复用
go
func main() {
//声明三个存放结果的channel
firstCh := make(chan string)
secondCh := make(chan string)
threeCh := make(chan string)
//同时开启3个goroutine下载
go func() {
firstCh <- downloadFile("firstCh")
}()
go func() {
secondCh <- downloadFile("secondCh")
}()
go func() {
threeCh <- downloadFile("threeCh")
}()
//开始select多路复用,哪个channel能获取到值,
//就说明哪个最先下载好,就用哪个。
select {
case filePath := <-firstCh:
fmt.Println(filePath)
case filePath := <-secondCh:
fmt.Println(filePath)
case filePath := <-threeCh:
fmt.Println(filePath)
}
}
func downloadFile(chanName string) string {
//模拟下载文件,可以自己随机time.Sleep点时间试试
time.Sleep(time.Second)
return chanName+":filePath"
}
// channel来退出
func testselectclose() {
ch := make(chan int)
go func() {
for i := 1; i <= 3; i++ {
ch <- i
}
close(ch)
// ch <- 1// panic: send on closed channel
}()
for {
select {
case val, ok := <-ch:
// 检测到false,因为close了,那么就会推出
if !ok {
fmt.Println("Channel closed, exiting.")
return
}
fmt.Println("Received:", val)
}
}
}