我知道像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
答案 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>