Ruby 1.8.7:Forks&管道 - 故障排除

时间:2013-06-09 20:45:49

标签: ruby fork pipe

我知道像Parallel这样有很棒的宝石,但是我想出了下面的课程作为练习。

它工作正常,但是在进行大量迭代时,有时Ruby会“卡住”。当按下CTRL + C时,我可以从回溯中看到它总是在第38或45行(两个Marshal线)。 你能看到任何错误吗?似乎管道是“悬挂”的,所以我想我可能会以错误的方式使用它们。

我的目标是使用有限数量的forks(max_forks)迭代一个数组(我作为'对象'传递)并返回一些值。另外我想保证所有孩子在被杀死时被杀死(即使在杀死-9的情况下),这就是为什么我引入了“life_line”管道(我在Stackoverflow上看过这里)这可能会成功。)

class Parallel

  def self.do_fork(max_forks, objects)
    waiter_threads = []
    fork_counter = []

    life_line = {}
    comm_line = {}

    objects.each do |object|
      key = rand(24 ** 24).to_s(36)

      sleep(0.01) while fork_counter.size >= max_forks

      if fork_counter.size < max_forks
        fork_counter << true

        life_line[key] = {}
        life_line[key][:r], life_line[key][:w] = IO.pipe

        comm_line[key] = {}
        comm_line[key][:r], comm_line[key][:w] = IO.pipe

        pid = fork {
          life_line[key][:w].close
          comm_line[key][:r].close

          Thread.new {
            begin
              life_line[key][:r].read
            rescue SignalException, SystemExit => e
              raise e
            rescue Exception => e
              Kernel.exit
            end
          }

          Marshal.dump(yield(object), comm_line[key][:w]) # return yield
        }

        waiter_threads << Thread.new {
          Process.wait(pid)

          comm_line[key][:w].close
          reply = Marshal.load(comm_line[key][:r])
          # process reply here
          comm_line[key][:r].close

          life_line[key][:r].close
          life_line[key][:w].close
          life_line[key] = nil

          fork_counter.pop 
        }
      end
    end

    waiter_threads.each { |k| k.join } # wait for all threads to finish
  end
end

2 个答案:

答案 0 :(得分:1)

错误是这样的:

管道只能处理一定数量的数据(例如64 KB)。 一旦你写了更多,管道将永远“卡住”。

一个简单的解决方案是在开始写入之前读取线程中的管道。

comm_line = IO.pipe

# Buffered Pipe Reading (in case bigger than 64 KB)
reply = ""
read_buffer = Thread.new {
  while !comm_line[0].eof?
    reply = Marshal.load(comm_line[0])
  end
}

child_pid = fork {
  comm_line[0].close
  comm_line[0].write "HUGE DATA LARGER THAN 64 KB"
}

Process.wait(child_pid)

comm_line[1].close
read_buffer.join
comm_line[0].close

puts reply # outputs the "HUGE DATA"

答案 1 :(得分:0)

我认为问题不在于元帅。更明显的一个似乎是你的fork可能在服务员线程到达之前完成执行(导致后者永远等待)。

尝试将Process.wait(pid)更改为Process.wait(pid, Process::WNOHANG)。如果没有可用的子节点(匹配给定的PID,如果有的话),则Process::WNOHANG标志指示Ruby不挂起。请注意,这可能并非在所有平台上都可用,但至少应该适用于Linux。

您的代码还存在许多其他潜在问题,但如果您只是“作为练习”提出它,它们可能无关紧要。例如,Marshal.load不喜欢遇到EOF,所以如果您希望有多个对象可供阅读,我可能会通过说Marshal.load(comm_line[key][:r]) unless comm_line[key][:r].eof?或循环until comm_line[key][:r].eof?来防范这些。 / p>

相关问题