目录
【1】定义一个管道
【2】向管道写入数据
【3】 从管道读取数据
【4】使用空接口的管道
【5】关闭管道
【6】遍历管道
【7】协程和管道的应用例子
【8】管道阻塞
【9】管道多路复用
package main
import "fmt"
func main() {
//定义一个管道只能存入int类型的数据
var intChan chan int
//管道必须make才能使用
intChan = make(chan int, 3)
fmt.Printf("intChan 的值是%v,地址是%p", intChan, &intChan)
}
可以看出管道是引用类型,他的值是内存中真正的管道的地址
管道的长度不能超过其容量否则会报以下错误
all goroutines are asleep - deadlock!
package main
import "fmt"
func main() {
//定义一个管道只能存入int类型的数据
var intChan chan int
//管道必须make才能使用
intChan = make(chan int, 3)
//向管道写入数据
intChan <- 10
num := 5
intChan <- num
//看看管道的长度和cap(容量)
fmt.Printf("intChan len = %v,cap = %v\n", len(intChan), cap(intChan))
}
管道的数据是先进先出的,所以第一个放入的数据也会被第一个读取
注意:读取时如果数据已被取完还继续取时会报以下错误
all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
package main
import "fmt"
func main() {
//定义一个管道只能存入int类型的数据
var intChan chan int
//管道必须make才能使用
intChan = make(chan int, 3)
// fmt.Printf("intChan 的值是%v,地址是%p\n", intChan, &intChan)
//向管道写入数据
intChan <- 10
num := 5
intChan <- num
intChan <- 66
//看看管道的长度和cap(容量)
fmt.Printf("intChan len = %v,cap = %v\n", len(intChan), cap(intChan))
//从管道中读取数据
var num2 int
num2 = <-intChan
num3 := <-intChan
<-intChan//直接取出不接收
fmt.Println("num2 = ", num2)
fmt.Println("num3 = ", num3)
fmt.Printf("intChan len = %v,cap = %v\n", len(intChan), cap(intChan))
}
使用空接口可以存入任何类型的数据,但是取出时必须使用类型断言
package main
import "fmt"
type Cat struct {
Name string
Age int
}
func main() {
allChan := make(chan interface{}, 10)
cat1 := Cat{"tom", 10}
cat2 := Cat{"jack", 18}
allChan <- cat1
allChan <- cat2
allChan <- 30
allChan <- "lucy"
<-allChan
catRes2 := (<-allChan).(Cat)
fmt.Printf("catRes的类型是%T,值是%v\n", catRes2, catRes2)
fmt.Println(catRes2.Name)
}
使用内置函数 close() 可以关闭 channel, 当 channel关闭后,就不能再向 channel写数据了,但是仍然可以从该 channel读取数据
channel支持for--range的方式进行遍历,请注意两个细节
1、在遍历时,如果channel没有关闭,则会出现deadlock的错误2、在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历。
package main
import "fmt"
func main() {
intChan := make(chan int, 200)
for i := 0; i < 100; i++ {
intChan <- i * 2
}
close(intChan)
for v := range intChan {
fmt.Println(v)
}
}
示例1:
goroutine和channel协同工作的案例
1、 开启一个writeData协程,向管道intChan中写入200个整数
2、开启一个readData协程,从管道intChan中读取writeData写入的数据。
3、注意: writeData和readDate操作的是同一个管道
4、主线程需要等待writeData和readDate协程都完成工作才能退出
package main
import "fmt"
func writeData(intChan chan int) {
for i := 1; i <= 200; i++ {
intChan <- i
fmt.Println("writeData写入数据 = ", i)
}
close(intChan)
}
func readData(intChan chan int, exitChan chan bool) {
for {
v, ok := <-intChan
if !ok {
break
}
fmt.Println("readData读到数据 = ", v)
}
//读完数据后,任务完成,向exitChan中写入一个true
exitChan <- true
close(exitChan)
}
func main() {
intChan := make(chan int, 200)
exitChan := make(chan bool, 1)
go writeData(intChan)
go readData(intChan, exitChan)
for {
_, ok := <-exitChan
if !ok {
break
}
}
}
示例2: 通过阻塞完成协程的信息通信
package _case
import "fmt"
func Communication() {
ch := make(chan int, 20)
go communicationF1(ch)
go communicationF2(ch)
}
// 接收一个只写通道
func communicationF1(ch chan<- int) {
for i := 0; i < 100; i++ {
ch <- i
}
close(ch)
}
// 接收一个只读通道
func communicationF2(ch <-chan int) {
for v := range ch {
fmt.Println(v)
}
}
package main
import (
"os"
"os/signal"
_case "project01/channel-select/case"
)
func main() {
_case.Communication()
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, os.Kill)
<-ch
}
如果只向管道写入数据,而没有读取,就会出现阻塞而deadlock,原因是intChan容量是10,而代码writeData会写入 50个数据,因此会阻塞在 wiiteData的 ch<-i
1、如果,编译器(运行),发现一个管道只有写,而没有读,则该管道,会阻塞。
2、写管道和读管道的频率不一致,无所谓。
// 通知协程退出与多路复用
func NoticeAndMultiplexing() {
ch := make(chan int, 1)
strCh := make(chan string, 1)
done := make(chan struct{}, 1)
go NoticeAndMultiplexingF1(ch)
go NoticeAndMultiplexingF2(strCh)
go NoticeAndMultiplexingF3(ch, strCh, done)
time.Sleep(5 * time.Second)
close(done)
}
func NoticeAndMultiplexingF1(ch chan<- int) {
for i := 0; i < 100; i++ {
ch <- i
}
}
func NoticeAndMultiplexingF2(ch chan<- string) {
for i := 0; i < 100; i++ {
ch <- fmt.Sprintf("字符串:%d", i)
}
}
// select 子句作为一个整体阻塞,其中任意channel准备就绪则继续执行
func NoticeAndMultiplexingF3(ch <-chan int, strCh <-chan string, done <-chan struct{}) {
i := 0
for {
select {
case i := <-ch:
fmt.Println(i)
case str := <-strCh:
fmt.Println(str)
case <-done:
fmt.Println("收到退出通知,退出当前协程")
return
// default:
// fmt.Println("不阻塞,进入死循环")
}
i++
fmt.Println("累计执行次数:", i)
}
}
调用:
package main
import (
"os"
"os/signal"
_case "project01/channel-select/case"
)
func main() {
_case.NoticeAndMultiplexing()
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, os.Kill)
<-ch
}
执行结果: