详解golang channel有无缓冲区的区别

来自:网络
时间:2024-03-30
阅读:

有无缓冲的区别

形象说明一下无缓冲和有缓冲的区别:

无缓冲是同步的,例如 make(chan int),就是一个送信人去你家门口送信,你不在家他不走,你一定要接下信,他才会走,无缓冲保证信能到你手上。

有缓冲是异步的,例如 make(chan int, 1),就是一个送信人去你家仍到你家的信箱,转身就走,除非你的信箱满了,他必须等信箱空下来,有缓冲的保证信能进你家的邮箱。

channel 无缓冲

例1:

func main() {
    ch := make(chan int)
    ch <- 1 // 报错,因为ch 无缓冲,存一个就必须立即取出来
    fmt.Println(<- ch)
}

改正:

func main() {
    ch := make(chan int)
    go tt(ch) // 开一个 goroutine
    fmt.Println("我先执行1111")
    fmt.Println(<-ch) // 因为前面开了一个 goroutine, 这一行比 go tt(ch) 先执行,这里堵塞了,等到 tt(ch) 中的语句执行完之后,本行执行
    // 我先执行1111
    // 我先执行2222
    // 1
}
 
func tt(ch chan int) {
    fmt.Println("我先执行2222")
    ch <- 1
}

例2:

package main
 
import "fmt"
 
func main() {
    ch := make(chan int) // 无缓冲的channel
    go unbufferChan(ch)
 
    for i := 0; i < 10; i++ {
        fmt.Println("receive ", <-ch) // 读出值
    }
}
 
func unbufferChan(ch chan int) {
    for i := 0; i < 10; i++ {
        fmt.Println("send ", i)
        ch <- i // 写入值
    }
}
 
// 输出
// send  0
// send  1
// receive  0
// receive  1
// send  2
// send  3
// receive  2
// receive  3
// send  4
// send  5
// receive  4
// receive  5
// send  6
// send  7
// receive  6
// receive  7
// send  8
// send  9
// receive  8
// receive  9

channel 带缓存

例1:

放的速度较快,先放满了 5 个,阻塞住;取的速度较慢,放了5个才开始取,由于缓冲区已经满了,所以取出一个之后,才能再次放入;放完了之后虽然缓冲区关闭了,但是缓冲区的内容还保留,所以还能继续取出

func put(c chan int) {
    for i := 0; i < 10; i++ {
       c <- i
       time.Sleep(100 * time.Millisecond)
       fmt.Println("->放入", i)
    }
    fmt.Println("=所有的都放进去了!关闭缓冲区,但是里面的数据不会丢失,还能取出。")
    close(c)
}
 
func main() {
    ch := make(chan int, 5)
    go put(ch)
    for {
       time.Sleep(1000 * time.Millisecond)
       data, ok := <-ch
       if ok == true {
          fmt.Println("<-取出", data)
       } else {
          break
       }
    }
}
 
// ->放入 0
// ->放入 1
// ->放入 2
// ->放入 3
// ->放入 4
// <-取出 0
// ->放入 5
// <-取出 1
// ->放入 6
// <-取出 2
// ->放入 7
// <-取出 3
// ->放入 8
// <-取出 4
// ->放入 9
// =所有的都放进去了!关闭缓冲区,但是里面的数据不会丢失,还能取出。
// <-取出 5
// <-取出 6
// <-取出 7
// <-取出 8
// <-取出 9

例2:一边存,一边取

package main
 
import"fmt"
 
var c = make(chan int, 5)
 
func main() {
    go worker(1)
    for i := 1; i < 10; i++ {
       c <- i
       fmt.Println(i)
       fmt.Println("cap = ", cap(c), " len = ", len(c))
    }
}
 
func worker(id int) {
    for {
       _ = <-c
    }
}
 
// 运行输出:
// 1
// cap =  5  len =  0
// 2
// cap =  5  len =  0
// 3
// cap =  5  len =  1
// 4
// cap =  5  len =  2
// 5
// cap =  5  len =  0
// 6
// cap =  5  len =  1
// 7
// cap =  5  len =  2
// 8
// cap =  5  len =  2
// 9
// cap =  5  len =  0
返回顶部
顶部