channel 是一种类型,一种引用类型
var 变量 chan 元素类型
var ch1 chan int // 声明一个传递整型的管道
var ch2 chan bol // 声明一个传递布尔型的管道
var ch3 chan []int // 声明一个传递 int 切片的管道
声明的管道后需要使用 make 函数初始化之后才能使用
make(chan 元素类型, 容量)
var ch = make(chan int, 4) //创建一个能存储 4 个 int 类型数据的管道
管道有发送(send)、接收(recive)和关闭(close)三种操作
发送和接收都使用<-符号
func TestChannel(t *testing.T) {
//channel 管道 是引用数据类型 先入先出
var ch = make(chan int, 4) //创建管道
ch <- 10 //把 10 发送到 ch 中
a := <-ch //获取管道的数据
fmt.Println(a) //10
//打印管道的长度 容量
fmt.Printf("值 %v 长度 %v 容量 %v \n", ch, len(ch), cap(ch)) //值 0xc000014200 长度 0 容量 4
ch <- 20
<-ch
}
关闭管道 close(ch)
关于关闭管道需要注意的事情是,只有在通知接收方 goroutine 所有的数据都发送完毕的时
候才需要关闭管道。管道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束
操作之后关闭文件是必须要做的,但关闭管道不是必须的。
关闭后的管道有以下特点:
无缓冲的管道:
如果创建管道的时候没有指定容量,那么我们可以叫这个管道为无缓冲的管道
无缓冲的管道又称为阻塞的管道
func TestChannel() {
//无缓冲的管道
ch := make(chan int)
ch <- 10
fmt.Println("发送成功")
}
//编译报错 fatl ero: al gorutines are asleep - deadlock!
//有缓冲的管道
ch := make(chan int,1)
ch <- 10
a1:=<-ch
fmt.Println(a1)//10
a2:=<-ch
//管道的阻塞 在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报告 deadlock
只要管道的容量大于零,那么该管道就是有缓冲的管道,管道的容量表示管道中能存放元素
的数量。就像你小区的快递柜只有那么个多格子,格子满了就装不下了,就阻塞了,等到别
人取走一个快递员就能往里面放一个。
var ch = make(chan int, 4)
for i := 0; i < 4; i++ {
ch <- i
}
close(ch)//for range 循环需要执行关闭管道,否则会报deallock
//循环遍历管道的值,注意管道没有key
for v := range ch {
fmt.Println(v)
}
for i := 0; i < 4; i++ {
fmt.Println(<-ch)
}
//二个协程 二个管道 边写数据 边读数据
import (
"fmt"
"sync"
"testing"
)
func ReadDate(ch chan int) {
for v := range ch {
fmt.Printf("读取管道数据%v\n",v)
}
wg.Done()
}
func WriteData(ch chan int) {
for i := 0; i < 10; i++ {
ch<-i
fmt.Printf("写入管道数据%v\n",i)
}
close(ch)
wg.Done()
}
var wg sync.WaitGroup
func TestChannel(t *testing.T) {
for v := range ch {
fmt.Println(v)
}
var ch1 = make(chan int,10)
wg.Add(1)
go WriteData(ch1)
wg.Add(1)
go ReadDate(ch1)
wg.Wait()
}
import (
"fmt"
"sync"
"testing"
)
//前10000数据存入管道
func putNum(ch chan int) {
for i := 2; i <= 10000; i++ {
ch <- i
}
close(ch)
wg.Done()
}
//生成素数
func DoNum(ch chan int, num chan int, status chan bool) {
for v := range ch {
var flag = true
for i := 2; i < v; i++ {
if v%i == 0 {
flag = false
break
}
}
if flag {
num <- v //num素数
}
}
status <- true //记录管道执行状态
wg.Done()
}
//打印素数
func PrintNum(ch chan int) {
for v := range ch {
fmt.Println(v)
}
wg.Done()
}
var wg sync.WaitGroup
func TestChannel(t *testing.T) {
var intChan = make(chan int, 1000)
var primeChan = make(chan int, 1000)
var exitChan = make(chan bool, 16)
wg.Add(1)
go putNum(intChan)
for i := 0; i < 16; i++ {//开启16 个协程处理素数判断
wg.Add(1)
go DoNum(intChan, primeChan, exitChan)
}
wg.Add(1)
//判断primeChan管道执行完成后关闭
go func() {
for i := 0; i < 16; i++ {
<-exitChan
}
close(primeChan)
wg.Done()
}()
wg.Add(1)
go PrintNum(primeChan)
wg.Wait()
}
有的时候我们会将管道作为参数在多个任务函数间传递,很多时候我们在不同的任务函数中 使用管道都会对其进行限制,比如限制管道在函数中只能发送或只能接收
//定义双向管道
var myChan = make(chan int,2)
//定义只读的channel
var readChan = make(<-chan int,2)
<-readChan
<-readChan
//定义只写的channel
var writeChan = make(chan<- int,2)
writeChan<-10
writeChan<-20
func TestChan(t *testing.T) {
//select 多路复用 在某些场景下我们需要同时从多个 channel接收数据,这个时候就可以用到select多路复用
var intChan = make(chan int,10)
var stringChan = make(chan string,6)
for i := 0; i < 10; i++ {
intChan<-i
}
for i := 0; i < 6; i++ {
stringChan<-"hello" +fmt.Sprintf("%d",i)
}
//使用select来获取channel里面的数据的时候不需要关闭channel,如果关闭会出现死循环
for{
select {
case v:=<-intChan:
fmt.Printf("读取int值%v\n",v)
time.Sleep(time.Millisecond*50)
case v:=<-stringChan:
fmt.Printf("读取string值%v\n",v)
time.Sleep(time.Millisecond * 50)
default:
fmt.Println("结束")
return
}
}
}
func sayNum() {
for i := 0; i < 10; i++ {
time.Sleep(time.Second)
fmt.Println("hello",i)
}
}
func test() {
//defer + recover 处理异常
defer func() {
if err:=recover();err!=nil{
fmt.Println("程序异常")
}
}()
var myMap map[int]string
myMap[0] = "hello world"
}
func TestChan(t *testing.T) {
go sayNum()
go test()
}