ruby - 为什么 JRuby 的 read_nonblock 会阻塞?

标签 ruby jruby nonblocking

我正在尝试向 JRuby 进程添加错误日志记录,该进程从一个子进程的标准输出读取数据,并将此数据写入另一个子进程的标准输入,同时收集数据的一些统计信息。使用 IO.popen4 生成子进程。

要读取错误流,我不能使用阻塞读取,因为在正常情况下这些流上没有输出。但是,当我在这些流上使用 read_nonblock 时,我仍然遇到 JRuby 中的阻塞读取。

为什么 read_nonblock 调用会阻塞?我如何重写这段代码,使其永不阻塞并始终显示子进程输出的任何标准错误?

下面是我正在使用的重现问题的代码的简化版本。它在 jruby 上阻塞并且不在 ruby 1.9.3p194(2012-04-20 修订版 35410)[x86_64-darwin11.4.0] 上显示预期输出。

if RUBY_PLATFORM != "java" && RUBY_VERSION =~ /^1\.9/
  class IO
    def self.popen4(*args, &block)
      require "open4"
      Open4::popen4(*args, &block)
    end
  end
end

IO.popen4('echo', 'hi') do |_, _, stdout1, stderr1|
  IO.popen4('sh', '-c', 'cat 1>&2') do |_, stdin2, _, stderr2|
    stdout1.each_line do |line|
      stdin2 << line
      (IO.select([stderr1, stderr2], [], [], 0.1) or [[]]).first.each do |stream|
        begin
          # in jruby 1.6.8 (ruby-1.9.2-p312) (2012-09-18 1772b40) (Java HotSpot(TM) Client VM 1.6.0_37) [darwin-i386-java], read_nonblock blocks
          # idem in jruby 1.7.2 (1.9.3p327) 2013-01-04 302c706 on Java HotSpot(TM) Client VM 1.6.0_37-b06-434-11M3909 [darwin-i386]
          puts stream.read_nonblock(1000)
        rescue Exception => e
          puts e.message
        end
      end
    end
  end
end

我知道它在 read_nonblock 中被阻塞,因为按下 ctrl+\时的堆栈跟踪:

"main" prio=5 tid=0000000003110800 nid=0xb0201000 runnable [00000000b01ff000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.FileDispatcher.read0(Native Method)
at sun.nio.ch.FileDispatcher.read(FileDispatcher.java:26)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:198)
at sun.nio.ch.IOUtil.read(IOUtil.java:171)
at sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:144)
- locked <0000000006158308> (a java.lang.Object)
at org.jruby.util.io.ChannelStream.refillBuffer(ChannelStream.java:196)
at org.jruby.util.io.ChannelStream.bufferedRead(ChannelStream.java:926)
at org.jruby.util.io.ChannelStream.bufferedRead(ChannelStream.java:888)
at org.jruby.util.io.ChannelStream.fread(ChannelStream.java:1288)
- locked <000000000615a8f8> (a org.jruby.util.io.ChannelStream)
at org.jruby.util.io.ChannelStream.readnonblock(ChannelStream.java:1314)
- locked <000000000615a8f8> (a org.jruby.util.io.ChannelStream)
at org.jruby.RubyIO.getPartial(RubyIO.java:2762)
at org.jruby.RubyIO.read_nonblock(RubyIO.java:2697)
at org.jruby.RubyIO$INVOKER$i$0$1$read_nonblock.call(RubyIO$INVOKER$i$0$1$read_nonblock.gen)
at org.jruby.internal.runtime.methods.JavaMethod$JavaMethodN.call(JavaMethod.java:646)
at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:204)
at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:168)
at read_nonblock_test.chained_2_rescue_1$RUBY$SYNTHETIC__file__(read_nonblock_test.rb:19)
at read_nonblock_test.block_3$RUBY$__file__(read_nonblock_test.rb:16)
at read_nonblock_test$block_3$RUBY$__file__.call(read_nonblock_test$block_3$RUBY$__file__)
at org.jruby.runtime.CompiledBlock19.yield(CompiledBlock19.java:139)
at org.jruby.runtime.Block.yield(Block.java:130)
...

最佳答案

所以,这是一个非常令人反感的解决方案,但它对我有用。由于 select 总是返回“yes”,而 nonblock 不起作用,我看不到其他选择:

      require 'timeout'
      begin
        while true
          line = nil
          Timeout::timeout(1) { line=io.gets }
          if line.nil?
            break
          else
            puts "line: #{line}"
          end
        end
      rescue Timeout::Error
      end

-罗宾

关于ruby - 为什么 JRuby 的 read_nonblock 会阻塞?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14697272/

相关文章:

php - 如何判断 curl_multi_exec 何时完成_发送_数据

ruby-on-rails - 两个表之间的连接在 Activerecord 中失败,出现未初始化的常量错误

ruby - 现有文本字段的 Rails 操作文本

ruby - 如何在 Ruby 中遍历循环

ruby - JRuby vs YARV 性能——显着差异?

javascript - Node.js 无阻塞地写入数组

ruby-on-rails - 使用具有单表继承的私有(private)方法

mysql - jruby jdbcmysql 适配器不适用于 ssl?

scala - Mirah 比 JRuby、Groovy 和 Scala 提供了什么?

multithreading - 扭曲/ Tornado 等如何工作