生产者与消费者模式-Golang实现

372 阅读4分钟

生产者与消费者模式

概述

生产者消费者问题是经典的并发同步问题。描述有若干生产者和若干消费者,生产者负责生产产品并将产品放入盒子(缓冲区),而消费者则消费盒子的产品。问题的关键为消费者之间、生产者之间、消费者与生产者之间的竞争,以及在“盒子”满了,应该停止(阻塞)生产者的生产,在“盒子”空了应该停止(阻塞)消费者的消费。传统的方式,我们需要用锁,信号量等来处理,而Go并不需要,通过Go的goroutine以及channel可以很方便的实现生产者消费者模型。

生产者与消费者,可以一对多,也可以多对一,也可以多对多,本节以最复杂情况多对多为例展开分析。

示例代码:

 package main
 ​
 import "fmt"
 ​
 func Producer(id int, ch chan int, done chan bool) {
     for i := 1; i <= 10; i++ {
         fmt.Printf("%v号生产者,产生数据: %v \n", id, i)
         ch <- i
     }
     done <- true
     //在开启多个goroutine进行生产时,不能在函数内部将通道关闭。
     //此处往done通道内写数据,目的为了表示子goroutine生产完毕
     //当在主函数中能把取出相应数量(numProduc)的元素时,即代表所有生产结束,通道可关闭
 }
 ​
 func Consumer(id int, ch chan int, done chan bool) {
     for v := range ch {
         fmt.Printf("%v号消费者,消费数据:%v \n", id, v)
     }
     done <- true
     //函数主体结束时,往通道里写一个数据。
     //当开启多个goroutine时,每个协程结束都往通道写一个数据,相当于告诉外界,我消费结束了。
     //在主函数末尾,依次取出该通道内数据,能当全部取出,就代表所有子goroutine结束,主程序也可退出。
     //相当于起到同步等待的作用
 }
 ​
 func main() {
     ch := make(chan int, 20)
     numProduc := 3  //以三个生产者为例
     numConsum := 5  //以五个消费者为例
     donePr := make(chan bool, numProduc) //donePr的容量即为生产者数量
     doneCo := make(chan bool, numConsum) //doneCo的容量即为消费者数量
 ​
     for i := 1; i <= numProduc; i++ {
         go Producer(i, ch, donePr)
     }
 ​
     for j := 1; j <= numConsum; j++ {
         go Consumer(j, ch, doneCo)
     }
 ​
     for ii := 0; ii < numProduc; ii++ {
         <-donePr //done通道内的数据可全部写出,代表所有生产结束,ch通道可关闭
     }
     close(ch)
 ​
     for jj := 0; jj < numConsum; jj++ {
         <-doneCo //把done通道内的数据写出,起到一个wait()的作用,等待子进程全部结束,之后主进程便可结束
     }
 }

输出结果:

 1号生产者,产生数据: 1 
 1号生产者,产生数据: 2 
 1号生产者,产生数据: 3 
 1号生产者,产生数据: 4 
 1号生产者,产生数据: 5 
 1号生产者,产生数据: 6 
 1号生产者,产生数据: 7 
 1号生产者,产生数据: 8 
 1号生产者,产生数据: 9 
 1号生产者,产生数据: 10 
 3号生产者,产生数据: 1 
 2号消费者,消费数据:3 
 2号消费者,消费数据:6 
 3号生产者,产生数据: 2 
 5号消费者,消费数据:1 
 3号生产者,产生数据: 3 
 5号消费者,消费数据:8 
 3号生产者,产生数据: 4 
 3号消费者,消费数据:4 
 3号消费者,消费数据:10 
 3号消费者,消费数据:1 
 3号消费者,消费数据:2 
 3号消费者,消费数据:3 
 3号消费者,消费数据:4 
 4号消费者,消费数据:5 
 3号生产者,产生数据: 5 
 3号生产者,产生数据: 6 
 3号生产者,产生数据: 7 
 3号生产者,产生数据: 8 
 3号生产者,产生数据: 9 
 3号生产者,产生数据: 10 
 2号生产者,产生数据: 1 
 2号生产者,产生数据: 2 
 2号生产者,产生数据: 3 
 2号生产者,产生数据: 4 
 2号消费者,消费数据:7 
 2号消费者,消费数据:7 
 2号消费者,消费数据:8 
 3号消费者,消费数据:5 
 3号消费者,消费数据:10 
 3号消费者,消费数据:1 
 3号消费者,消费数据:2 
 3号消费者,消费数据:3 
 3号消费者,消费数据:4 
 2号生产者,产生数据: 5 
 2号生产者,产生数据: 6 
 2号生产者,产生数据: 7 
 2号生产者,产生数据: 8 
 2号生产者,产生数据: 9 
 1号消费者,消费数据:2 
 1号消费者,消费数据:6 
 1号消费者,消费数据:7 
 4号消费者,消费数据:6 
 4号消费者,消费数据:9 
 3号消费者,消费数据:5 
 1号消费者,消费数据:8 
 2号生产者,产生数据: 10 
 2号消费者,消费数据:9 
 4号消费者,消费数据:10 
 5号消费者,消费数据:9