Ruby多线程生产者 - 消费者

时间:2013-02-14 04:09:35

标签: ruby multithreading producer-consumer

我不到一周前开始使用Ruby,但已经开始使用了 欣赏语言的力量。我正在尝试经典 生产者 - 消费者问题,实施为橙树(c.f. http://pine.fm/LearnToProgram/?Chapter=09)。橘子树各生长 直到它死亡并且每年产生随机数量的橙子 (监制)。只要树上有任何东西,就可以采摘橘子 (消费者)。

我在这里遇到两个问题:

  1. 以下代码给出了以下异常(无法附加,无选项):

    /Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:84:
    warning: instance variable @orange_tree not initialized
    /Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:84:in `':
    
    undefined method `age' for nil:NilClass (NoMethodError) from
    /Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:45:in `'
    
  2. 我不确定多线程部分是否正确编码。

  3. 我有几本书,包括“编程Ruby”和“Ruby编程语言”,但它们都没有包含真正的“生产者 - 消费者问题”。

    P.S:为了充分披露,我也在Ruby论坛上发布了这个问题。但是,我已经看到了这里提供的优秀答案和/或建议,并希望我也能得到其中的一些。

    require 'thread'
    
    class OrangeTree
    GROWTH_PER_YEAR = 1
    AGE_TO_START_PRODUCING_ORANGE = 3
    AGE_TO_DIE = 7
    ORANGE_COUNT_RELATIVE_TO_AGE = 50
    def initialize
      @height = 0
      @age = 0
      @orange_count = 0
    end
    
    def height
      return @height
    end
    
    def age
      return @age
    end
    
    def count_the_oranges
      return @orange_count
    end
    
    def one_year_passes
      @age += 1
      @height += GROWTH_PER_YEAR
      @orange_count = Math.rand(@age..AGE_TO_DIE) * Math.log(@age) * ORANGE_COUNT_RELATIVE_TO_AGE
    end
    
    def pick_an_orange
      if (@age == AGE_TO_DIE)
        puts "Sorry, the Orange tree is dead"
      elsif (@orange_count > 0)
        @orange_count -= 1
        puts "The Orange is delicious"
      else
        puts "Sorry, no Oranges to pick"
      end
    end
    
    end
    
    class Worker
      def initialize(mutex, cv, orange_tree)
      @mutex = mutex
      @cv = cv
      @orange_tree = orange_tree
    end
    
    def do_some_work
      Thread.new do
        until (@orange_tree.age == OrangeTree.AGE_TO_DIE)
          @mutex.synchronize do
            sleep_time = rand(0..5)
            puts "Orange picker going to sleep for #{sleep_time}"
            sleep(sleep_time)
            puts "Orange picker woke up after sleeping for #{sleep_time}"
            @orange_tree.pick_an_orange
            puts "Orange picker waiting patiently..."
            @cv.wait(@mutex)
          end
        end
      end
    
      Thread.new do
        until (@orange_tree.age == OrangeTree.AGE_TO_DIE)
          @mutex.synchronize do
            sleep_time = rand(0..5)
            puts "Age increaser going to sleep for #{sleep_time}"
            sleep(sleep_time)
            puts "Age increaser woke up after sleeping for #{sleep_time}"
            @orange_tree.one_year_passes
            puts "Age increaser increased the age"
            @cv.signal
          end
        end
      end
    end
    
    Worker.new(Mutex.new, ConditionVariable.new, OrangeTree.new).do_some_work
    until (@orange_tree.age == OrangeTree.AGE_TO_DIE)
      # wait for the Threads to finish
    end
    
    end
    

1 个答案:

答案 0 :(得分:0)

@orange_tree是Worker对象的实例变量,只能从对象内部访问。您试图在“直到”状态下从全局范围访问它,它不存在。有几种方法可以解决它,但这个方法需要的变化最少:

...

orange_tree = OrangeTree.new
Worker.new(Mutex.new, ConditionVariable.new, orange_tree).do_some_work
until (orange_tree.age == OrangeTree::AGE_TO_DIE)
...

您还应该查看Thread#joinhttp://www.ruby-doc.org/core-1.9.3/Thread.html#method-i-join。它将照顾你的等待。其余的多线程代码在技术上看起来是正确的。我相信你知道,如果这不仅仅是一个练习,你会想让互斥体更精细。正如他们现在所说的那样,两个线程几乎完全相互执行,哪种线程会破坏线程点。

另外,请查看attr_accessorattr_reader。他们将节省您手动定义OrangeTree#heightage

的费用

多个加入示例

threads = []

threads << Thread.new do
  ...
end

threads << Threads.new do
  ...
end

threads.each { |thread| thread.join }