与子流程讨论,使用Ruby与IO和线程

时间:2010-03-02 11:51:30

标签: ruby process multithreading io popen

我正在尝试使用IO.popen来放置(使用.puts方法)并从进程到其子进程获取(使用.gets方法)消息。

我没有经过实验,我有一个问题。有了以下代码,我有一个错误,因为无法在封闭的流中写入。

class Interface
  def initialize(path)
    @sub_process = IO.popen(path, 'w+')
  end

  def start!
    if ok?
      @sub_process.puts 'Hello', 'my name is ...'
      # and more...
    end
  end

  protected

  def ok?
    is_ready?(@sub_process) && is_cool?(@sub_process)
  end

  def is_ready?(sub_process)
    reply = process_command(sub_process, 'are u ready?')
    reply.chomp.match(/yes_i_am_ready$/)
  end

  def is_cool?(sub_process)
    reply = process_command(sub_process, 'are u cool?')
    reply.chomp.match(/yes_i_am_cool$/)
  end

  def process_command(sub_process, command)
    rdr = Thread.new { sub_process.read } # alternative: io.readlines
    sub_process.puts "#{command}"
    sub_process.close_write
    rdr.value # joins and fetches the result
  end
end

a = Interface.new("./program")
a.start!
  `write'中的

(...):没有打开写入(IOError)

正如我们所看到的,这个错误发生在is_cool期间?测试(如:http://ruby-doc.org/core/classes/IO.html#M002289所述)。

但是如果我尝试在process_command方法中注释该行:

# sub_process.close_write

脚本似乎在无聊地睡觉:s

我认为不可能再打开一个封闭的流。而且我不能创建我的程序“./program”的其他IO.popen实例,因为它需要在开始时使用某些命令(比如“你准备好了吗?”和“你很酷吗?”)进行初始化。我使用它(通过简单的讨论发送和接收消息)。

如何更改当前代码以解决此问题?

编辑:换句话说,我想建立一个这样的沟通(根据给定的协议):

Parent message:                Child answer:
--------------                 ------------

'are u ready?'                 'yes_i_am_ready'
'are u cool?'                  'yes_i_am_cool'
'Hello'                        'foo'
'my name is ...'               'bar'

非常感谢您的帮助。

3 个答案:

答案 0 :(得分:2)

也许有一个有效的例子会有所帮助。这是一个经过测试并且已知在Linux上的MRI 1.8.7中工作的人。

<强> bar.rb

#!/usr/bin/ruby1.8

begin
  loop do
    puts "You said: #{gets}"
    $stdout.flush
  end
rescue Errno::EPIPE
end

<强> foo.rb

#!/usr/bin/ruby1.8

class Parent

  def initialize
    @pipe = IO.popen(CHILD_COMMAND, 'w+')
  end

  def talk(message)
    @pipe.puts(message)
    response = @pipe.gets
    if response.nil?
      $stderr.puts "Failed: #{CHILD_COMMAND}"
      exit(1)
    end
    response.chomp
  end

  private

  CHILD_COMMAND = './bar.rb'

end

parent = Parent.new
puts parent.talk('blah blah blah')
puts parent.talk('foo bar baz')

foo.rb输出

You said: blah blah blah
You said: foo bar baz

答案 1 :(得分:0)

关闭的IO不能再使用了。如果您打算继续使用它,则不应关闭IO

如果您删除了IO#close_write,则代码仍然存在以下问题。

rdr = Thread.new { sub_process.read }

IO#read读取直至EOF。所以在流关闭之前它永远不会终止。您在代码中提到IO#readline,这将是更好的选择。如果popend进程永远不会发送换行符,那么使用IO#readline程序只会挂起。

popen的另一个问题如下。 IO#popen创建一个新流程。进程可能会被您,其他用户,内存短缺等所杀死。不要指望您的流程始终始终运行。如果该进程被终止IO#readline将抛出EOFErrorIO#read将返回imidiatley。您可以使用以下代码确定终止原因。

Process::wait(io.pid)
status= $?
status.class # => Process::Status
status.signaled? # killed by signal?
status.stopsig # the signal which killed it
status.exited # terminated normal
status.exitstatus # the return value
status.ki

答案 2 :(得分:0)

使用Thread.new的这种形式是否有帮助?

rdr = Thread.new(sub_process) {|x| x.readlines }