> 文章列表 > GO语言小锤硬磕.二十二 管道(channel)

GO语言小锤硬磕.二十二 管道(channel)

GO语言小锤硬磕.二十二 管道(channel)

1.认识channel

        管道本质是一个队列(先进先出

        管道是线程安全的(自带锁)

        管道解决协程中资源同步问题

2.声明和初始化Channel

package main
import "fmt"
func main(){
//1.声明管道  var 变量名 chan 数据类型    var mych chan int
//2.初始化管道    make(chan 数据类型,管道容量) //管道和切片、字典一样make开辟存储空间后才能使用//管道和切片、字典一样是引用类型mych = make(chan int,3)
//3.查看管道的长度和容量fmt.println("长度是",len(mych),"容量是",cap(mych))
//4.管道写入数据mych<- 666
//5.取出管道中的数据num := <-mych
//注意1.管道中只能存放声明的数据类型。2.主线程中管道中已经没有数据,并且检测不到其他线程写入数据,再取会报错。3.主线程中管道数据已满,再写入也会报错。
}

3.管道的关闭和遍历

package main
import "fmt"
func main(){
//1.创建一个管道mych := make(chan int,3)
//2.写入数据mych<- 666mych<- 777mych<- 888
//3.关闭管道,关闭后只能读不能写close(mych)
//4.使用for range 遍历管道。使用前必须先关闭管道否则会报deadlock的错误//管道的遍历会弹出管道数据,长度会减少for value := range mych{fmt.Println(value)} //5.其他方式 ok-idiom模式读取管道,判断管道是否关闭,如果关闭会返回false给okfor{if num,ok:= <-mych; !ok{break;}else{fmt.Println(num)}}
//6.误区管道正常读取一次,内容会弹出,长度(len(mych))会减少1//for i:=0; i<len(mych); i++ {   //不正确}

4.Channel阻塞现象

        单独在主线程中操作管道,写满了会报错,没有数据去获取也会报错

        只要在协程中操作管道过,写满了就会阻塞,没有数据去获取也会阻塞

5.使用Channe实现生产者消费者

package main
import("fmt""math/rand""time"
)
//定义缓冲区
var mych = make(chan int,5)
var exitch = make(chan bool,1)//定义生产者
func producer(){rand.Seed(time.Now().UnixNano())for i:=0;i<10;i++{num:=rand.Intn(100)fmt.Println("生产者生产",num)//写入管道mych<-num}//生产完毕关闭管道close(mych)fmt.Println("生产者停止生产")
}
//定义消费者
func consumer(){//不断从管道中获取数据,直到管道关闭for{if num,ok := <-mych;!ok{break}else{fmt.Println("消费了",num)}}fmt.Println("消费者停止消费")exitch<-true}
func main(){go producer()go consumer()fmt.Println("exitch之前代码")<-exitchfmt.Println("exitch之后代码")
}

6.无缓冲Channel

package main
import "fmt"
var mych1 = make(chan int,5) //无缓冲区
var mych2 = make(chan int,0) //无缓冲区
fuc mian(){
//无缓冲区,需要在子程中提前埋入读取,而后写入go func(){                  //1.使用匿名函数建立协程fmt.Println(<-mych2)    //2.读取,读取到打印,读取不到线程挂起}()mych2<-1                    //3.写入
//注意//写入后在同一个线程读取会报错//fmt.Println(<-mych2)//在主程中先写入,在子程中后读取也会报错
}

7.有缓冲管道和无缓冲管道

        有缓冲管道具备异步打的能力(写几个读一个或读几个)

        无缓冲管道具备同步的能力(写一个读一个)

8.单向管道和双向管道

        默认情况下所有的管道都是双向的

        单向管道应用于管道作为参数传递时,限制管道的写入和读取权限。

双向管道 var  mych  chan int = make(chan int,0)

只写管道var  mych chan<- int = make(chan<- int,0)

只读管道var  mych <-chan int = make(<-chan int,0)

双向管道可以转换为任意一种单向管道,单向管道不能转换为双向管道

package main
import "fmt"
func main(){//1.定义一个双向管道var mych chan int = make(chan int,5)//2.双向管道转单向var mych1 chan<- intmych1=mychvar mych2 <-chan intmych2=mych//3.注意 管道之间赋值是地址传递,以上三个管道底层指向相同容器
}

9.单向管道作为函数参数

package main
import("fmt""math/rand""time"
)//定义生产者
func producer(mych chan<- int){rand.Seed(time.Now().UnixNano())for i:=0;i<10;i++{num:=rand.Intn(100)fmt.Println("生产者生产",num)//写入管道mych<-num}//生产完毕关闭管道close(mych)fmt.Println("生产者停止生产")
}
//定义消费者
func consumer(mych <-chan int){//不断从管道中获取数据,直到管道关闭for{if num,ok := <-mych;!ok{break}else{fmt.Println("消费了",num)}}fmt.Println("消费者停止消费")
}
func main(){var mych = make(chan int,5)go producer(mych)consumer(mych)
}