一光年

[Go-goroutine介绍] 3. 有缓冲通道

2019.06.27

与无缓冲通道相对应的,是有缓冲通道(buffered channel),下面是他们的简单区分

无缓冲通道

  • 接收方:没有数据写入通道时,goroutine处理将会被阻塞
  • 发送方:没有阻塞

有缓冲通道

  • 接收方:没有数据写入通道时,goroutine处理将会被阻塞
  • 发送方:当缓冲已写满时,goroutine处理将会被阻塞

你家里养了3只母鸡,它们很聪明,每次下单都会自己下到鸡蛋篮子里。但篮子大小有限,每次只能放10个。 所以这3只鸡每次下蛋填满了篮子后就会休息,直到你一个一个的把鸡蛋从篮子里拿走。拿走一个,它们就可以再下一个蛋。

下面的实例程序实现了一个简单的生产消费者的模型。

package main

import (
    "fmt"
    "sync"
    "time"
)

var wg2 sync.WaitGroup

func main() {

    products := make(chan int, 5)

    wg2.Add(2)

    go produce(products)
    go consume(products)

    wg2.Wait()

    fmt.Println("\nTerminating Program")
}

func produce(products chan int) {

    defer wg2.Done()

    for i := 0; i < 10; i++ {
        fmt.Printf("\n生产产品 %d", i)
        products <- i
    }

    // time.Sleep(time.Duration(1000) * time.Millisecond)

    // 关闭通道
    fmt.Printf("\n生产者关闭通道并退出")
    close(products)
}

func consume(products chan int) {

    defer wg2.Done()

    for {
        time.Sleep(time.Duration(500) * time.Millisecond)
        product, ok := <- products
        if !ok {
            fmt.Printf("\n消费者退出")
            return
        }

        fmt.Printf("\n消费产品 %d", product)
    }
}

生产者在生产完10件商品后,就关闭通道并退出了。留下消费者慢吞吞的每0.5秒去消费1件商品,直到消费完所有商品后,goroutine处理退出。

程序运行的输入如下:

生产产品 0 生产产品 1 生产产品 2 生产产品 3 生产产品 4 生产产品 5 消费产品 0 生产产品 6 消费产品 1 生产产品 7 消费产品 2 生产产品 8 消费产品 3 生产产品 9 消费产品 4 生产者关闭通道并退出 消费产品 5 消费产品 6 消费产品 7 消费产品 8 消费产品 9 消费者退出

从输入可以观察得到以下几点:

  1. 在消费者开始消费前(等待了0.5秒),生产者已经生产了5件商品(缓冲区大小为5),生产完成后处理被阻塞
  2. 消费者开始消费后,每消费1件商品,生产品就再生产1一件商品(消费速度太慢,生产者在等待)
  3. 生产完10件商品后,生产者关闭了通道并退出。此时的缓冲区仍然剩下5件商品供消费者一件件的慢慢消费