【问题标题】:Ruby Multithreaded producer-consumerRuby 多线程生产者-消费者
【发布时间】:2013-02-14 04:09:34
【问题描述】:

我不到一周前开始使用 Ruby,但已经开始使用 欣赏语言的力量。我正在尝试经典 生产者-消费者问题,实现为橙树(c.f. 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. 我不确定多线程部分的编码是否正确。

我自己有几本书,包括“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

【问题讨论】:

    标签: ruby multithreading producer-consumer


    【解决方案1】:

    @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 }
    

    【讨论】:

    • 在进行了您建议的更改(包括加入)之后,我现在得到: Orange picker going to sleep for 5 Orange Picker 在睡了 5 后醒来 抱歉,没有 Oranges 来挑选 Orange picker 耐心等待.. . 增龄器要睡2 增龄器在睡2 后醒来 /Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:76:in join': deadlock detected (fatal) from /Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:76:in do_some_work' from...
    • 是在第二个join 上吗?是不是和上面的例子一样?
    • 这里有一些代码 sn-p(希望我知道如何格式化) end orange_picker.join age_increaser.join end worker = Worker.new(Mutex.new,ConditionVariable.new,OrangeTree.new) worker .do_some_work 结束
    • 看起来不错。调试死锁可能很困难。我将首先注释掉两个线程中可能的所有内容。如果这“修复”了死锁,则逐行取消注释并查看导致死锁的行。 (我显然看不到 @cv.wait@cv.signal 在做什么,但我有预感问题就在那里。)
    • 我使用 MonitorMixin 重写了代码,然后重新使用 Mutex,结果相同。我使用了 2 个 ConditionVariable,结果相同 :( 如果它不起作用,那肯定不是因为缺乏尝试。
    猜你喜欢
    • 2017-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多