前言
生产者/消费者问题是并发编程中的经典问题,也被称为有限缓冲区问题,此模型中有生产者和消费者两种组件:
-
生产者不断生成一定量的数据放到缓冲区中。
-
消费者不断消耗缓冲区中的数据。
注意:
-
缓冲区的大小是有限的,当缓冲区满了后,生产者进入阻塞状态。当缓冲区为空时,消费者进入阻塞状态。
-
同一份数据不能被多次消费。
-
多消费者和多生产者场景时,应该合理设计代码,避免数据竞争。
使用锁和条件变量
package main
import (
"fmt"
"sync"
)
//设置缓冲区为10
var data []int = make([]int, 10)
var mutex sync.Mutex
var cond *sync.Cond = sync.NewCond(&sync.Mutex{}) //消费者条件变量
var condFull *sync.Cond = sync.NewCond(&sync.Mutex{}) //生产者条件变量
var in, out int = 0, 0 //消费者和生产者的插入位置
var now = 0 //现有资源计数
var num = 0 //已生成资源计数
var wg sync.WaitGroup
func Consumer(name string) {
for i := 0; i < 30; i++ {
//加锁,避免数据竞争
mutex.Lock()
//当现有资源为0时,阻塞等待唤醒
if now == 0 {
fmt.Println(name, "暂停")
mutex.Unlock()
cond.L.Lock()
cond.Wait()
cond.L.Unlock()
mutex.Lock()
}
fmt.Println(name, "消费", out, "位置的数据:", data[out])
out = (out + 1) % 10
now--
mutex.Unlock()
//有空位了,唤醒一个阻塞的生产者
condFull.Signal()
}
wg.Done()
}
func Provider(name string) {
for i := 0; i < 30; i++ {
//加锁,避免数据竞争
mutex.Lock()
//如果缓冲区满了,阻塞等待唤醒
if now == len(data) {
fmt.Println(name, "暂停")
mutex.Unlock()
condFull.L.Lock()
condFull.Wait()
condFull.L.Unlock()
mutex.Lock()
}
fmt.Println(name, "生产", in, "位置的数据", num)
data[in] = num
num++
in = (in + 1) % 10
now++
mutex.Unlock()
//唤醒一个阻塞的消费者
cond.Signal()
}
wg.Done()
}
func main() {
wg.Add(6)
go Consumer("c1")
go Consumer("c2")
go Consumer("c3")
go Provider("p1")
go Provider("p2")
go Provider("p3")
wg.Wait()
}