生产者消费者模式

6次阅读

共计 1982 个字符,预计需要花费 5 分钟才能阅读完成。

1、什么是生产者 - 消费者模式?

在实际开发过程中,我们可能遇到如下场景:某些模块负责生产数据,某些模块负责处理数据,这些模块可能是一些进程、线程、类或者是函数。其中生产数据的模块称之为生产者,处理数据的模块称之为消费者。
上面的描述还不能解释生产者 - 消费者模式,该模式在生产者和消费者之间还需要一个缓冲区,生产者把数据放入缓冲区,消费者从缓冲区取出数据。

为了更好的理解该模式,我们举一个生活中的例子,假如你要寄一封信,大致的流程如下:

  1. 你把信写好——相当于生产者生产数据
  2. 你把写好的信放入邮箱——相当于生产者把数据放入缓冲区
  3. 快递员从邮箱中取出信件——相当于消费者从缓冲区取出数据
  4. 快递员把信拿到邮局做相应的处理——相当于消费者处理数据

这样一解释是不是更加清楚、通俗易懂呢。

2、该模式有什么作用?

解释清楚什么是生产者 - 消费者模式之后,我们应该有这些的疑问:这个模式有什么用呢?生产者和消费者之间为什么还要一个缓冲区呢,生产者生产数据后直接通知消费者处理数据不就完事了吗?等等。
1、解耦
假设生产者和消费者是两个类,它俩之间不存在缓冲区,那么当生产者生产数据后直接调用消费者类的某个方法去处理数据,生产者就会严重依赖消费者(耦合),如果有一天消费者类中的业务逻辑发生变化,那么可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间就不存依赖关系,耦合也就降低了。
2、支持并发
该模式最好的优势就是处理高并发。在该模式下,生产者和消费者是两个独立运作的个体,生产者把数据往缓冲区一丢,就可以立即去生产下一个数据,不用等待消费者去处理。
如果缓冲区不存在,那么每次生产者生产数据直接调用消费者去处理数据,只有消费者处理完数据后生产者才能继续生产下一个数据,这就造成了阻塞。

3、Node 实现一个简单的案例

在实现该模式之前需要注意以下两点:

  1. 当数据填满缓冲区后生产者就不能继续生产数据了,此时应该让他休眠,等缓冲区不满时唤醒生产者。
  2. 当缓冲区数据为空时,消费者取不到数据,此时应该让他休眠,等待缓冲区有数据时再唤醒消费者。

本文基于 napajs 模块实现生产者 - 消费者模式:
想了解 napajs 模块可以参考:
https://github.com/microsoft/napajs
https://juejin.im/post/59e9b8faf265da430b7a5efd

const napa = require('napajs')

// 创建生产者和消费者线程
var producer = napa.zone.create('producer', {workers: 2})
var consumer = napa.zone.create('consumer', {workers: 2})
// 创建共享区
var store = napa.store.create('store')

store.set('value', 0) // 产品数量
store.set('size', 5) // 缓冲区大小

function sleep() {
  return new Promise(resolve => {setTimeout(resolve, 1000)
  })
}

// 生产
function produce() {var store = global.napa.store.get('store')
  while(1) {if (store.get('value') < store.get('size')) {store.set('value', store.get('value') + 1)
      sleep().then(() => {console.log('生产者加一操作')
      })
    } else {continue}
  }
}

// 消费
function consume() {var store = global.napa.store.get('store')
  while(1) {if (store.get('value') > 0) {store.set('value', store.get('value') - 1)
      sleep().then(() => {console.log('消费者减一操作')
      })
    } else {continue}
  }
}
// 注册方法
producer.broadcast(produce.toString())
consumer.broadcast(consume.toString())

// 执行
producer.execute(() => {global.produce()})
consumer.execute(() => {global.consume()})

上述代码不一定正确,因为本人在引入 napajs 过程中遇到了一些棘手问题,仅仅作为示例加深理解。

在网站上搜到一篇博客使用 napajs 实现了该模式,可以借鉴参考:
https://blog.csdn.net/changhuzhao/article/details/78422336

正文完
 0