GO并发编程 使用锁和条件变量实现生产者/消费者模型

前言

生产者/消费者问题是并发编程中的经典问题,也被称为有限缓冲区问题,此模型中有生产者和消费者两种组件:

  • 生产者不断生成一定量的数据放到缓冲区中

  • 消费者不断消耗缓冲区中的数据

注意:

  • 缓冲区的大小是有限的,当缓冲区满了后,生产者进入阻塞状态。当缓冲区为空时,消费者进入阻塞状态。

  • 同一份数据不能被多次消费。

  • 多消费者和多生产者场景时,应该合理设计代码,避免数据竞争。

使用锁和条件变量

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()
}

相关文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨雨不怕雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值