【问题标题】:Returning data from forked processes从分叉的进程返回数据
【发布时间】:2010-11-07 18:08:39
【问题描述】:

如果我这样做了

Process.fork do 
  x 
end 

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

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

【问题讨论】:

    标签: ruby process fork


    【解决方案1】:

    我们实际上只需要在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,因为它们不支持分叉。

    【讨论】:

      【解决方案2】:

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

      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

      【讨论】:

      • 您看到我在回答中提供的隔离测试链接了吗?它可以处理分叉和非分叉环境,并且可能已经完成了您需要的一切。
      【解决方案3】:

      我将一路上找到的所有解决方案(一些其他问题,例如用户退出 + 管道缓冲区)打包到 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
      

      【讨论】:

      • 你能在这里传递对象甚至类作为参数吗?我真的可以使用一种方法来分叉一个进程并将整个环境传递给它。
      【解决方案4】:

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

      我推荐the aw gem

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

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

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

      【讨论】:

        【解决方案5】:

        根据文档:

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

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

        【讨论】:

        • 这对获取“x”返回的内容有何帮助?
        • 子进程在一个单独的进程中运行(很明显),因此要获得“x”所做的任何事情的值,您可能必须通过套接字、管道或类似的东西进行通信。我怀疑您是否可以,例如,在块内设置反映在块外的变量,因为子进程具有单独的内存等。
        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

          如果孩子只需要一小块 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  :-)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-08-15
            • 1970-01-01
            • 2021-11-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多