26

如果我做

Process.fork do 
  x 
end 

我怎么知道 x 返回了什么(例如 true/fase/string)?

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

4

7 回答 7

33

实际上,我们只需要在Rails 隔离测试中处理这个问题。我在我的博客上发布了一些关于它的信息。

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

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

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

另请注意,这里的许多细节 ( read.close, Process.wait2(pid)) 主要是管理细节,所以如果你经常使用它,你可能应该把它移到一个可以重用的实用程序库中。

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

于 2009-07-02T20:09:59.650 回答
12

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

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 插件中看到它

于 2009-07-02T20:54:34.913 回答
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
于 2009-08-15T12:51:53.897 回答
1

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

我推荐宝石aw

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

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

arr = ['foo']
Aw.fork! { arr << 'FUU' } # => ["foo", "FUU"]
arr # => ["foo"]
于 2015-11-10T17:10:11.613 回答
0

根据文档:

如果指定了块,则该块在子进程中运行,并且子进程以零状态终止。

所以如果你用块调用它,它返回0。否则,它的功能与Unix上的系统调用基本相同fork()(父接收新进程的PID,子接收nil)。

于 2009-07-02T19:37:45.410 回答
0

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

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

于 2009-07-02T19:46:49.090 回答
0

如果孩子只需要一小块 ruby​​ 代码,您可以使用共享内存来执行此操作。像下面这样的东西会起作用:

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  :-)
于 2009-07-02T20:06:17.803 回答