【Go】十八、管道

1、管道

  • channel
  • 本质是一个队列,先进先出
  • 自身线程安全,多协程访问时,不用加锁,channel本身就是线程安全的
  • 一个string的管道只能存放string类型数据

在这里插入图片描述

2、管道的定义

var  变量名  chan  数据类型
  • chan是管道的关键字
  • 数据类型,比如int类型的管道只能写入整数int
  • 管道是引用类型,必须初始化才能写入数据,即make后才能使用
  • 管道中不能存放大于容量的数据
  • 在没有使用协程的情况下,如果管道的数据已经全部取出,那么再取就会报错

示例:

package main
import(
        "fmt"
)
func main(){
        //定义管道 、 声明管道 ---> 定义一个int类型的管道
        var intChan chan int
        //通过make初始化:管道可以存放3个int类型的数据
        intChan = make(chan int,3)
        //证明管道是引用类型:
        fmt.Printf("intChan的值:%v",intChan) // 0xc000112080
        //向管道存放数据:
        intChan<- 10
        num := 20
        intChan<- num
        intChan<- 40  
        //注意:不能存放大于容量的数据:
        //intChan<- 80  
        //在管道中读取数据:
        num1 := <-intChan
        num2 := <-intChan
        num3 := <-intChan
        fmt.Println(num1)
        fmt.Println(num2)
        fmt.Println(num3)
        //注意:在没有使用协程的情况下,如果管道的数据已经全部取出,那么再取就会报错:
        num4 := <-intChan
        fmt.Println(num4)
        //输出管道的长度:
        fmt.Printf("管道的实际长度:%v,管道的容量是:%v",len(intChan),cap(intChan))
}

3、管道的关闭

使用内置函数close可以关闭管道,当管道关闭后,就不能再向管道写数据了,但是仍然可以从该管道读取数据

package main
import(
        "fmt"
)
func main(){
        //定义管道 、 声明管道
        var intChan chan int
        //通过make初始化:管道可以存放3个int类型的数据
        intChan = make(chan int,3)
        //在管道中存放数据:
        intChan<- 10
        intChan<- 20
        //关闭管道:
        close(intChan)
        //再次写入数据:--->报错
        //intChan<- 30
        //当管道关闭后,读取数据是可以的:
        num := <- intChan
        fmt.Println(num)
}

4、管道的遍历

  • for
  • for-range

在遍历时,如果管道没有关闭,则报错:

在这里插入图片描述

package main
import(
        "fmt"
)
func main(){
        //定义管道 、 声明管道
        var intChan chan int
        //通过make初始化:管道可以存放100个int类型的数据
        intChan = make(chan int,100)
        for i := 0;i < 100;i++ {
                intChan<- i
        }
        //在遍历前,如果没有关闭管道,就会出现deadlock的错误
        //所以我们在遍历前要进行管道的关闭
        close(intChan)
        //遍历:for-range
        for v := range intChan {
                fmt.Println("value = ",v)
        }
}

5、管道 + 协程

  • 开启一个writeData协程,向管道中写入50个整数.
  • 开启一个readData协程,从管道中读取writeData写入的数据

在这里插入图片描述

package main
import(
        "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()	
}

运行:
在这里插入图片描述

在没有使用协程的情况下,如果管道的数据已经全部取出,那么再取就会报错。所以上面即使写入后sleep两秒,读取也不会报错。

6、只读、只写管道

package main
import(
        "fmt"
)
func main(){
        //默认情况下,管道是双向的--》可读可写:
        //var intChan1 chan int
        //声明为只写:
        var intChan2 chan<- int  // 管道具备<- 只写性质
        intChan2 = make(chan int,3)
        intChan2<- 20
        //num := <-intChan2 报错
        fmt.Println("intChan2:",intChan2)
        //声明为只读:
        var intChan3 <-chan int// 管道具备<- 只读性质 
        if intChan3 != nil {	//非空
                num1 := <-intChan3
                fmt.Println("num1:",num1)
        }
        //intChan3<- 30 报错
        
}

7、管道的阻塞

管道只写入数据,不读取,超过容量:

package main
import(
        "fmt"
        _"time"
        "sync"
)
var wg sync.WaitGroup //只定义无需赋值

//写:
func writeData(intChan chan int){
        defer wg.Done()
        for i := 1;i <= 20;i++{   //超过容量管道的容量10
                intChan<- i
                fmt.Println("写入的数据为:",i)
                //time.Sleep(time.Second)
        }
        //管道关闭:
        close(intChan)
}

func main(){//主线程
        //定义管道:
        intChan := make(chan int,10)
        wg.Add(1)
        //开启写的协程:
        go writeData(intChan)
        wg.Wait()	
}

在这里插入图片描述

改一下,写的快,读的慢:没报错,但很明显,写被影响了,到最后被动等读协程,读走一个,写协程才能继续写。

在这里插入图片描述

8、select

  • 从多个管道中进行非阻塞的选择
  • select同时监听多个管道的数据流通,并在其中任意一个管道有数据可读或者可写的时候做相应的处理
select {
case <- channel1:
    // 处理channel1的数据
case data := <- channel2:
    // 处理channel2的数据
case channel3 <- value:
    // 向channel3发送数据
default:
    // 当没有任何通道准备就绪时执行default块
}

按顺序检查有没那个case后面的管道是可读或者可写的,有则执行该case语句。如果多个case同时满足,Go会随机选择一个执行。

如果没有任何case语句满足条件,且存在default语句,则执行default块中的代码。如果没有default语句,则select语句会被阻塞,直到至少有一个case语句满足条件。

package main
import(
        "fmt"
        "time"
)
func main(){
        //定义一个int管道:
        intChan := make(chan int,1)
        go func(){
                time.Sleep(time.Second * 5)
                intChan<- 10
        }()
        //定义一个string管道:
        stringChan := make(chan string,1)
        go func(){
                time.Sleep(time.Second * 2)
                stringChan<- "msbgolang"
        }()
        //fmt.Println(<-intChan)//本身取数据就是阻塞的
        select{
                case v := <-intChan :
                        fmt.Println("intChan:",v)
                case v := <-stringChan :
                        fmt.Println("stringChan:",v)
                default:
                        fmt.Println("防止select被阻塞")
        }
}

输出:

防止select被阻塞

上面两个case的协程都不能立即可读或可写,走了default。没有default,则输出:

msbgolang

因为其对应的管道阻塞时间短,2s后就可以读到了

相关推荐

  1. go从0到1项目实战体系go管理

    2024-04-02 17:36:03       41 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-02 17:36:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-02 17:36:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-02 17:36:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-02 17:36:03       20 阅读

热门阅读

  1. 深入了解Flutter中的Sealed Class及其使用

    2024-04-02 17:36:03       14 阅读
  2. linux脚本打包

    2024-04-02 17:36:03       12 阅读
  3. python系列教程220——哪种迭代最快

    2024-04-02 17:36:03       14 阅读
  4. 问答系统开发:基于深度学习的文本理解与生成

    2024-04-02 17:36:03       13 阅读
  5. 【leetcode面试经典150题】2.移除元素(C++)

    2024-04-02 17:36:03       10 阅读
  6. R语言提取站点的nc文件时间序列数据

    2024-04-02 17:36:03       13 阅读
  7. 每日一题 --- 用栈实现队列[力扣][Go]

    2024-04-02 17:36:03       13 阅读
  8. docker-打包&部署

    2024-04-02 17:36:03       9 阅读
  9. Python 踩坑记

    2024-04-02 17:36:03       16 阅读
  10. 设计模式-单例模式(饿汉式)

    2024-04-02 17:36:03       13 阅读