在Ruby中实现生产者 - 消费者模式

时间:2015-04-21 03:06:35

标签: ruby multithreading

假设我有200个昂贵的方法调用(每个都有不同的参数)。出于某种原因,我可以并行执行其中5个调用,但不能再执行。我可以一次执行一个,但每次执行5次的速度要快5倍。

我想永远执行五件事。我不想排队五,等到所有五个完成,然后排队五个。如果我排队A,B,C,D,E和C先完成,我想立即用F替换它,即使A和B还没有完成。

我一直在阅读这个问题,因为这是我可以想象的定期发生的事情。解决方案似乎是生产者 - 消费者模式,Ruby在其标准库中内置了一些结构,用于该模式(QueueSizedQueue)。我玩过代码示例,阅读了一些文档,我想我对它有一个粗略的了解。但我有一些问题,我对我的解决方案并不自信,而且多线程的整个领域对我来说都是新的基础,所以我想我会在这里要求确保我不是,你知道,完全错了,只是幸运

所以这是我写的测试程序:

q = Queue.new
q << 'balloon'
q << 'sandwich'
q << 'clown'
q << 'fairy floss'
q << 'ferris wheel'
q << 'magician'
q << 'cake'
q << 'present'
q << 'chip'
q << 'game'
q << 'animal'

consumer_1 = Thread.new do
  until q.empty?
    sleep rand(0..10)
    print "#{q.pop}\n"
  end
end

# consumer 2 and 3 are identical to consumer 1

[consumer_1, consumer_2, consumer_3].map(&:join)

队列中包含生日聚会所需的一系列内容。 3个消费者线程在列表中工作。

这有效,我的问题是:

如果消费者的数量决定了并行工作的项目数量,那么拥有大小的队列有什么意义呢?

大小的队列是否仅在任务无限,未知或数量巨大并且您想在填充队列之前暂停的情况下才有用?

我未能正确解决问题吗?手动创建多个线程然后在它们上面调用join似乎有些混乱。有更好的解决方案吗?

1 个答案:

答案 0 :(得分:1)

SizedQueue可以防止生产者更快地添加商品,然后消费者可以消费。

<强> SizedQueue#push

  

如果队列中没有剩余空间,则等待空间变为空间   可用

Queue#pop / SizedQueue#pop

  

如果队列为空,则调用线程将暂停,直到数据为止   推到队列上。

SizedQueue vs Queue

require 'thread'
require 'logger'

queue = SizedQueue.new(3)
#queue = Queue.new # goes berzerk
logger = Logger.new(STDOUT)

Thread.new do
  item = 0
  loop do
    item += 1
    queue << item
    logger.info "#{item} produced"
  end
end

consumers = 2.times.map do |i|
  Thread.new do
    loop do
      item = queue.pop
      logger.info "consumed #{item}"
      sleep item
    end
  end
end

consumers.each(&:join)

如何停止工作线程

require 'thread'
require 'logger'

queue = Queue.new
logger = Logger.new(STDOUT)
consumers_count = 5

end_object = BasicObject.new

consumers = consumers_count.times.map do
  Thread.new do
    until (item = queue.pop) == end_object
      logger.info "consumed #{item}"
    end
  end
end

1000.times.each { |item| queue << item }
consumers_count.times { queue << end_object }

consumers.each(&:join)

进一步阅读: