0

不到一周前,我开始使用 Ruby,但已经开始欣赏这种语言的力量。我正在尝试解决一个经典的生产者-消费者问题,以橙树的形式实现(参见 http://pine.fm/LearnToProgram/?Chapter=09)。橘子树每年都在生长,直到它死去,并且每年生产随机数量的橘子(生产者)。只要树上有橙子就可以采摘(消费者)。

我这里有两个问题:

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

    /Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:84:
    警告:实例变量@orange_tree 未初始化
    /Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:84:in `':
    
    nil:NilClass (NoMethodError) 的未定义方法‘age’来自
    /Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:45:in `'
    
  2. 我不确定多线程部分是否正确编码。

我自己有几本书,包括“Programming Ruby”和“The Ruby Programming Language”,但没有一本包含真正的“生产者-消费者问题”。

PS:为了充分披露,我也在 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
4

1 回答 1

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#height,age等。

多连接示例

threads = []

threads << Thread.new do
  ...
end

threads << Threads.new do
  ...
end

threads.each { |thread| thread.join }
于 2013-02-14T06:43:18.207 回答