从分叉进程返回数据

时间:2009-07-02 19:32:18

标签: ruby process fork

如果我这样做

Process.fork do 
  x 
end 

我怎么知道x返回的内容(例如true / fase / string)?

(写入文件/数据库不是一种选择......)

7 个答案:

答案 0 :(得分:31)

我们实际上只需要在Rails isolation testing中处理这个问题。我发布了一些on my blog

基本上,您要做的是在父级和子级中打开管道,并让子级写入管道。这是在子进程中运行块的内容并返回结果的简单方法:

def do_in_child
  read, write = IO.pipe

  pid = fork do
    read.close
    result = yield
    Marshal.dump(result, write)
    exit!(0) # skips exit handlers.
  end

  write.close
  result = read.read
  Process.wait(pid)
  raise "child failed" if result.empty?
  Marshal.load(result)
end

然后你可以运行:

do_in_child do
  require "some_polluting_library"
  SomePollutingLibrary.some_operation
end

请注意,如果在子级中执行require,则无法访问父级中的该库,因此无法使用此方法返回该类型的对象。但是,您可以返回两者中可用的任何类型。

另请注意,此处的许多详细信息(read.closeProcess.wait2(pid))主要是内务管理细节,因此如果您经常使用此内容,则应将其移至实用程序库中,重用。

最后请注意,这不适用于Windows或JRuby,因为它们不支持分叉。

答案 1 :(得分:11)

感谢所有答案,我启动并运行了我的解决方案,仍然需要了解如何处理非分叉环境,但现在它可以工作:)

read, write = IO.pipe
Process.fork do
  write.puts "test"
end
Process.fork do
  write.puts 'test 2'
end

Process.wait
Process.wait

write.close
puts read.read
read.close

你可以在行动@ parallel_specs Rails plugin

中看到它

答案 2 :(得分:10)

我将我发现的所有解决方案(用户退出+管道缓冲区等其他一些问题)包装到ruby parallel gem中。现在它很简单:

results = Parallel.map([1,2,3],:in_processes=>4) do |i|
  execute_something(i)
end

results = Parallel.map([1,2,3],:in_threads=>4) do |i|
  execute_something(i)
end

答案 3 :(得分:1)

是的,你可以创建一个子进程来执行一个块。

我建议the aw gem

Aw.fork! { 6 * 7 } # => 42

当然,它可以防止副作用:

arr = ['foo']
Aw.fork! { arr << 'FUU' } # => ["foo", "FUU"]
arr # => ["foo"]

答案 4 :(得分:0)

根据文件:

  

如果指定了一个块,则该子块在子进程中运行,子进程终止,状态为零。

因此,如果使用块调用它,则返回0.否则,它的功能与Unix上的fork()系统调用基本相同(父进程接收新进程的PID,子进程接收{{ 1}})。

答案 5 :(得分:0)

两个Unix进程之间的fork通信主要是返回代码,仅此而已。但是,您可以在两个进程之间打开一个文件描述符,并通过此文件描述符在进程之间传递数据:这是正常的Unix管道方式。

如果你传递Marshal.dump()和Marshal.load()值,你可以轻松地在这些Ruby进程之间传递Ruby对象。

答案 6 :(得分:0)

如果孩子只需要一小块红宝石代码,你可以使用共享内存来完成这项工作。以下内容将起作用:

str = 'from parent'

Thread.new do
  str = 'from child'
end

sleep(1)

puts str    # outputs "from child"
然而,并发可能相当棘手,并且以这种方式访问​​共享内存是一个很大的原因 - 任何时候你有一个变量而另一个进程可能会从你身下改变它,你应该非常谨慎。或者,你可以使用一个更繁琐的管道,但除了最简单的代码之外,它可能更安全,并且还可以用来运行任意命令。这是一个例子,直接来自IO.popen的rdoc:

f = IO.popen("uname")
p f.readlines     # outputs "Darwin", at least on my box  :-)