title: Implementing the producer-consumer pattern in Ruby
body: <p>Let's say that I have 200 expensive method calls to make (each with different arguments). For some reason, I can execute 5 of those calls in parallel, but no more. I <em>could</em> execute them one at a time, but doing 5 at a time is 5 times faster. </p>
  
  <p>I want to always be executing five things. I don't want to queue five, wait until all five are done, and then queue five more. If I queue A,B,C,D,E and C finishes first, I want to immediately replace it with F, even if A and B aren't done yet.</p>
  
  <p>I've been reading into this problem, since it's something I can imagine happening on a regular basis. The solution seems to be the producer-consumer pattern, and Ruby has some structures built into its standard library for use with that pattern (<code>Queue</code> and <code>SizedQueue</code>). I've played around with code samples, read some of the documentation, and I think I have a rough understanding of it. But I have some questions I'm not confident in my solution, and the whole area of multithreading is new ground for me, so I thought I'd ask here to make sure I'm not, you know, totally wrong and just lucky.</p>
  
  <p>So here's a test program I wrote:</p>
  
  <pre><code>q = Queue.new
  q &lt;&lt; 'balloon'
  q &lt;&lt; 'sandwich'
  q &lt;&lt; 'clown'
  q &lt;&lt; 'fairy floss'
  q &lt;&lt; 'ferris wheel'
  q &lt;&lt; 'magician'
  q &lt;&lt; 'cake'
  q &lt;&lt; 'present'
  q &lt;&lt; 'chip'
  q &lt;&lt; 'game'
  q &lt;&lt; '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(&amp;:join)
  </code></pre>
  
  <p>The queue holds a list of things we need for a birthday party. 3 consumer threads work through the list.</p>
  
  <p>This works, my questions are:</p>
  
  <p>If it's the number of consumers that determines how many items are being worked in parallel, what's the point of having a <em>sized</em> queue? </p>
  
  <p>Would a sized queue only be useful in situations where the tasks are infinite, unknown, or vast in number and you want to pause before filling the queue up? </p>
  
  <p>Have I failed to implement the problem properly? Creating multiple threads by hand and then calling <code>join</code> on them seems slightly messy. Is there a better solution?</p>