(1)管道本质就是一个数据结构-队列
(2)数据是先进先出
(3)自身线程安全,多协程访问时,不需要加锁,channel本身就是线程安全的
(4)管道有类型的,一个string的管道只能存放string类型数据
var 变量名 chan 数据类型
PS1:chan管道关键字
PS2:数据类型指的是管道的类型,里面放入数据的类型,管道是有类型的,int类型的管道只能写入整数int
PS3:管道是引用类型,必须初始化才能写入数据,即make后才能使用
func main() { //定义管道 、 声明管道 ---> 定义一个int类型的管道 var intChan chan int //通过make初始化:管道可以存放3个int类型的数据 intChan = make(chan int, 3) //证明管道是引用类型: fmt.Printf("intChan的值: %v /n",intChan) //向管道存放数据: intChan <- -10 num := 20 intChan <- num intChan <- 40 //注意:不能存放大于容量的数据: // intChan <- -80 //输出管道的长度: fmt.Printf("管道的实际长度:%v,管道的容量是:%v /n",len(intChan),cap(intChan)) //在管道中读取数据: num1 := <-intChan num2 := <-intChan num3 := <-intChan fmt.Println(num1) fmt.Println(num2) fmt.Println(num3) //注意:在没有使用协程的情况下,如果管道的数据已经全部取出,那么再取就会报错: // num4 := <-intChan // fmt.Println(num4) fmt.Printf("管道的实际长度:%v,管道的容量是:%v /n",len(intChan),cap(intChan))}
使用内置函数close可以关闭管道,当管道关闭后,就不能再向管道写数据了,但是仍然可以从该管道读取数据。
func main() { var intChan chan int intChan = make(chan int, 3) intChan <- 10 intChan <- 20 //关闭管道: close(intChan) //再次写入数据:--->报错 // intChan <- 30 //当管道关闭后,读取数据是可以的: num := <- intChan fmt.Println(num)}
管道支持for-range的方式进行遍历,请注意两个细节
1)在遍历时,如果管道没有关闭,则会出现deadlock的错误
2)在遍历时,如果管道已经关闭,则会正常遍历数据,遍历完后,就会退出遍历。
func main() { var intChan chan int intChan = make(chan int, 100) for i := 0; i < 100; i++ { intChan <- i } //在遍历前,如果没有关闭管道,就会出现deadlock的错误 //所以我们在遍历前要进行管道的关闭 // for v := range intChan { // fmt.Println("value = ",v) // } close(intChan) //遍历:for-range for v := range intChan { fmt.Println("value = ",v) }}
请完成协程和管道协同工作的案例,具体要求:
1) 开启一个writeData协程,向管道中写入50个整数.
2) 开启一个readData协程,从管道中读取writeData写入的数据。
3) 注意: writeData和readDate操作的是同一个管道
4) 主线程需要等待writeData和readDate协程都完成工作才能退出
package mainimport ( "fmt" "time" "sync")var wg sync.WaitGroup//写:func writeData(intChan chan int) { defer wg.Done() for i := 1; i <= 50; i++ { intChan <- i fmt.Println("写入的数据为:",i) time.Sleep(time.Second) } close(intChan)}//读:func readData(intChan chan int) { defer wg.Done() for v := range intChan { fmt.Println("读取的数据为:",v) time.Sleep(time.Second) }}func main() { //主线程 //写协程和读协程共同操作同一个管道-》定义管道: intChan := make(chan int, 50) wg.Add(2) //开启读和写的协程: go writeData(intChan) go readData(intChan) //主线程一直在阻塞,什么时候wg减为0了,就停止 wg.Wait() fmt.Println("读写数据完成...")}
运行结果:
package mainimport ( "fmt")func main() { //默认情况下,管道是双向的--》可读可写: //声明为只写: // 管道具备<- 只写性质 var intChan chan<- int intChan = make(chan int, 3) intChan <- 10 // 报错 // num := <- intChan fmt.Println("intChan:",intChan) //声明为只读: // 管道具备<- 只读性质 var intChan2 <-chan int if intChan2 != nil { num1 := <- intChan2 fmt.Println("num1:",num1) } // 报错 // intChan2 <- 30}
package mainimport ( "fmt" "sync")var wg sync.WaitGroupfunc writeData(intChan chan int) { defer wg.Done() for i := 1; i < 10; i++ { intChan <- i fmt.Println("写入的数据:",i) } close(intChan)}func readData(intChan chan int) { defer wg.Done() for v := range intChan { fmt.Println("读取的数据为:",v) }}func main() { intChan := make(chan int, 10) wg.Add(2) go writeData(intChan) // go readData(intChan) wg.Wait()}
运行结果
package mainimport ( "fmt" "sync" "time")var wg sync.WaitGroupfunc writeData(intChan chan int) { defer wg.Done() for i := 1; i < 10; i++ { intChan <- i fmt.Println("写入的数据:",i) } close(intChan)}func readData(intChan chan int) { defer wg.Done() for v := range intChan { fmt.Println("读取的数据为:",v) time.Sleep(time.Second) }}func main() { intChan := make(chan int, 10) wg.Add(2) go writeData(intChan) go readData(intChan) wg.Wait()}
PS:case后面必须进行的是io操作,不能是等值,随机去选择一个io操作
PS:default防止select被阻塞住,加入default
package mainimport ( "fmt" "time")func main() { intChan := make(chan int, 1) go func () { time.Sleep(time.Second * 15) intChan <- 15 }() stringChan := make(chan string, 1) go func () { time.Sleep(time.Second * 12) stringChan <- "hellocyz" }() //本身取数据就是阻塞的 // fmt.Println(<-intChan) select { case v := <-intChan : fmt.Println("intChan:",v) case v := <-stringChan : fmt.Println("stringChan:",v) default: fmt.Println("防止select被阻塞") }}
package mainimport ( "fmt" "time")//输出数字:func printNum() { for i := 1; i <= 10; i++ { fmt.Println(i) }}//做除法操作:func divide() { defer func () { err := recover() if err != nil { fmt.Println("devide()出现错误:",err) } }() num1 := 10 num2 := 0 result := num1 / num2 fmt.Println(result)}func main() { //启动两个协程: go printNum() go divide() time.Sleep(time.Second * 5)}
结果:
本文链接:http://www.28at.com/showinfo-26-5170-0.html聊聊协程和管道—管道
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: 如何使用Kafka构建事件驱动的架构