zoukankan      html  css  js  c++  java
  • Golang并发编程有缓冲通道和无缓冲通道(channel)

    无缓冲通道

    是指在接收前没有能力保存任何值得通道。
    这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作。如果两个goroutine没有同时准备好,通道会导致先执行发送或接收操作的goroutine阻塞等待。
    这种对通道进行发送和接收的交互行为本身就是同步的,其中任意一个操作都无法离开另一个操作单独存在。

    上图所示,如同接力赛。根据图编号观察①两个协程,创建好了通道②一个往通道里放,这时候两边阻塞④这时候另一个协程要接⑤另一个协程取出来,从①-⑤都是阻塞的,⑥才完成交接,才不会阻塞。

    再比喻: 就是一个送信人去你家门口送信 ,你不在家 他不走,你一定要接下信,他才会走。

    无缓冲channel创建

    ch := make(chan int, 0)  //第二个参数为0,或者不写第二个参数

    如果没有指定缓冲区容量,那么该通道就是同步的,因此会阻塞到发送者准备好发送和接收者准备好接收。

    代码案例

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        //创建一个无缓存的channel
        ch := make(chan int, 0)
    
        //len(ch)缓冲区剩余数据个数, cap(ch)缓冲区大小,两者这里永远都是0
        fmt.Printf("len(ch) = %d, cap(ch)= %d
    ", len(ch), cap(ch))
    
        //新建协程
        go func() {
            for i := 0; i < 3; i++ { //写三次
                fmt.Printf("子协程:i = %d
    ", i)
                ch <- i //往chan写内容
                fmt.Printf("len(ch) = %d, cap(ch)= %d
    ", len(ch), cap(ch))
            }
        }()
    
        //延时2秒
        time.Sleep(2 * time.Second)
    
        for i := 0; i < 3; i++ { //必须读三次
            num := <-ch //读管道中内容,没有内容前,阻塞
            fmt.Println("num = ", num)
        }
    
    }
    len(ch) = 0, cap(ch)= 0
    子协程:i = 0
    num =  0
    len(ch) = 0, cap(ch)= 0
    子协程:i = 1
    len(ch) = 0, cap(ch)= 0
    子协程:i = 2
    num =  1
    num =  2

    流程分析

    主协程执行到这里,阻塞两秒
    //延时2秒
    time.Sleep(2 * time.Second)
    
    //两秒钟时间子协程肯定把for循环执行完毕,但这里也会出现阻塞
    //阻塞原因是:当执行到往通道写数据是无缓冲的,对方不读之前会阻塞。也就是,在主协程等着子协程写完,但是主协程还没到读的时候,这时候出现阻塞,等到主协程读完数据才会往下走。
    可以执行观察一下程序的执行卡顿观察阻塞
    for i := 0; i < 3; i++ { //写三次 fmt.Printf("子协程:i = %d ", i) ch <- i //往chan写内容 fmt.Printf("len(ch) = %d, cap(ch)= %d ", len(ch), cap(ch)) }

    打印结果分析:首先子协程for循环往管道里写入一个数据,紧接着主协程for循环出现阻塞,然后主协程for循环从管道读数据,读完了打印。主协程打印完,子协程for循环继续给管道数据,但也有可能主协程读完数据没来得及打印,子协程就把数据写入管道并打印完毕,因为两个是同时并行的。

    有缓冲通道

     指通道可以保存多个值。

    如果给定了一个缓冲区容量,那么通道就是异步的,只要缓冲区有未使用空间用于发送数据,或还包含可以接收的数据,那么其通信就会无阻塞地进行

    上图所示:

    ①右侧的goroutine正在从通道接收一个值。
    ②右侧的goroutine独立完成了接手值得动作,而左侧的goroutine正在发送一个新值到通道里。
    ③左侧的goroutine还在向通道发送新值,而右侧的goroutine正在从通道接收另一个值。这个步骤里的两个操作既不是同步,也不会互相阻塞。
    ④所有的发送和接收都完成,而通道里还有几个值,也有一些空间可以存储更多的值

    有缓冲channel创建

    ch := make(chan int, 3)  //容量是3

    代码案例

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        //创建一个有缓存的channel
        ch := make(chan int, 3) //容量是3
    
        //len(ch)缓冲区剩余数据个数, cap(ch)缓冲区大小
        fmt.Printf("len(ch) = %d, cap(ch)= %d
    ", len(ch), cap(ch))
    
        //新建协程
        go func() {
            for i := 0; i < 10; i++ { //这里数据量大于管道容量,会出阻塞
                ch <- i //往chan写内容,如果主协程没读的话,写满3个就会阻塞在此
                fmt.Printf("子协程[%d]: len(ch) = %d, cap(ch)= %d
    ", i, len(ch), cap(ch))
            }
        }()
    
        //延时
        time.Sleep(2 * time.Second)
    
        for i := 0; i < 10; i++ { //这里数据量大于管道容量,会出阻塞
            num := <-ch //读管道中内容,没有内容前,阻塞
            fmt.Println("num = ", num)
        }
    
    }
    len(ch) = 0, cap(ch)= 3
    子协程[0]: len(ch) = 1, cap(ch)= 3
    子协程[1]: len(ch) = 2, cap(ch)= 3
    子协程[2]: len(ch) = 3, cap(ch)= 3
    num =  0
    num =  1
    num =  2
    num =  3
    子协程[3]: len(ch) = 3, cap(ch)= 3
    子协程[4]: len(ch) = 0, cap(ch)= 3
    子协程[5]: len(ch) = 1, cap(ch)= 3
    子协程[6]: len(ch) = 2, cap(ch)= 3
    子协程[7]: len(ch) = 3, cap(ch)= 3
    num =  4
    num =  5
    num =  6
    num =  7
    num =  8
    子协程[8]: len(ch) = 3, cap(ch)= 3
    子协程[9]: len(ch) = 0, cap(ch)= 3
    num =  9

    总结一下有缓冲channel和无缓冲channel的特点与不同

    无缓冲的与有缓冲channel有着重大差别,那就是一个是同步的 一个是非同步的。

    比如
    c1:=make(chan int) 无缓冲
    c2:=make(chan int,1) 有缓冲
    c1<-1 

    无缓冲: 不仅仅是向 c1 通道放 1,而是一直要等有别的携程 <-c1 接手了这个参数,那么c1<-1才会继续下去,要不然就一直阻塞着。
    有缓冲: c2<-1 则不会阻塞,因为缓冲大小是1(其实是缓冲大小为0),只有当放第二个值的时候,第一个还没被人拿走,这时候才会阻塞。

     

  • 相关阅读:
    吴裕雄 Bootstrap 前端框架开发——Bootstrap 辅助类:表格单元格使用了 "bg-info" 类
    吴裕雄 Bootstrap 前端框架开发——Bootstrap 辅助类:表格单元格使用了 "bg-success" 类
    吴裕雄 Bootstrap 前端框架开发——Bootstrap 辅助类:表格单元格使用了 "bg-primary" 类
    吴裕雄 Bootstrap 前端框架开发——Bootstrap 辅助类:"text-danger" 类的文本样式
    吴裕雄 Bootstrap 前端框架开发——Bootstrap 辅助类:"text-warning" 类的文本样式
    吴裕雄 Bootstrap 前端框架开发——Bootstrap 辅助类:"text-info" 类的文本样式
    吴裕雄 Bootstrap 前端框架开发——Bootstrap 辅助类:"text-success" 类的文本样式
    吴裕雄 Bootstrap 前端框架开发——Bootstrap 辅助类:"text-primary" 类的文本样式
    吴裕雄 Bootstrap 前端框架开发——Bootstrap 辅助类:"text-muted" 类的文本样式
    Power management in semiconductor memory system
  • 原文地址:https://www.cnblogs.com/wt645631686/p/9657711.html
Copyright © 2011-2022 走看看