生产者-消费者模型
一个重点在关注数据上的建模. 原先最朴素的做法是把生产和消费是串行并且集成在同一个模块内 —— 即生产完成后直接进行消费, 那么此时生产和消费在执行时序上是紧密关联的.
然后生产者-消费者模式是把生产和消费两个行为解耦,使得他们可以在不同的时间执行(去年的数据今年使用),以及可以“缓解”生产和消费数据执行的时候存在的时间差异
生产者和消费者
先说什么事生产者(Producer)和消费者(Consumer) —— 简单来说, 生产者是生产数据的,然后消费者是消费数据的(说了等于没说)
也就是说,有一条数据,我们需要在生产者处完成生产(不一定是构造出某个数据实例), 然后把这个数据交付给消费者使用(不一定是销毁某个实例)
实际上就是描述数据流动方向 —— Producer -> Consumer
比较值得一提的是,P和C不是绑定某个线程名称,只是说明数据是怎么被生产和消费的!!
问题与处理
那么来表现这个数据流其实非常简单 ——
auto data = Produce();
Consume(data)确乎如此,非常简单,不过这样就意味着 Consume 在处理数据的时候, Produce 只能干瞪眼, 哪怕其实什么数据等已经准备好了 —— 比如大量人抢单 Key Live, Producer 在此处创建大量订单, 但是由于每一次的 Consume 执行都需要等待 Produce 执行, 而 Produce 每次想要重新执行也要等待 Consume 结束. 所以就会导致系统每次只能接受并且处理一位敌人的订单. 而又因为 Consume 方法可能比 Produce 方法更耗时(比如要检查座位/票数余量/更新系统状态等),就会导致效率极其低下.
于是比较直观的, 引入一个 Buffer 进行存储, 我们“解偶”生产者和消费者效率速度差异带来的等待. 让 Producer 生产的数据先存进 Buffer, 然后自己继续不断创建; 让 Consumer 从 Buffer 中读取需要消费的数据来进行处理即可. 就不必让 Producer 和 Consumer 你等我我等你,拉低效率
那么现在的数据链条成了 —— Producer -> Buffer -> Consumer
并发语境下
上面聚焦的是数据流动的事情, 现在说点工作分配问题 —— 我们可以把生产和消费看成两个任务,一般我们会让两个任务执行在不同的上下文中(比如不同的线程),这样就可以不错地提高计算资源利用率的可能性.
也就是说目前至少有两个线程(Producer,Consumer)在对同一个对象进行读写处理(也就是 Buffer), 那么这就非常贴合我们说的数据竞态问题.
首先假设下 —— “先进入的数据先进行消费”, 这是典型的 FIFO, 那么显然队列就非常符合系统要求,所以打上“线程安全”的 Patch, 就是一个非常不赖的并发语境下的生成-消费者模型的 Demo.
那么朴素的,我们可以使用锁+队列实现,或者用什么无锁或者其他更神奇的设计进行实现.
当然,我们如果不假定先进入的数据先进行消费,而是有一个优先顺序在的话,用什么优先队列然后加上处理数据竞态的方法也是可以的.
此处只是想要表达 —— 该模型仅仅针对数据流而言 —— 那么怎么组织数据看情况, 如果出现数据竞态的话怎么设计安全的数据结构也看情况,
总结
综上所述,就是把简单的生产->消费这个数据链条变成生产->Buffer->消费这样的链条以使得系统更灵活与高效
- Producer 负责产生数据
- Consumer 负责处理数据
- Buffer 放在两者中间暂时保存数据
然后 Buffer 的主要作用是 ——
- 让 Producer 和 Consumer 可以相对独立地执行
- 缓冲两者短时间内的速度差异
- 作为两者之间的数据交接位置
如果 Producer 和 Consumer 位于不同线程,那么还需要进一步解决 Buffer 的线程安全,空时如何等待, 满时如何处理等问题,具体再进行分析罢