1

alarm(2)Ruby で POSIX (または類似のもの)への移植可能なインターフェースを探しています。つまり、バックグラウンド タイマーを設定して、n秒後に現在のプロセスにシグナルを送信できるようにしたいと考えています。

2006 年の ruby​​-talk リストで、 を使用した解決策を提供する良い議論を見つけましdl/importたが、これはちょっとしたハック (巧妙なハックではありますが) であり、あまり移植性がありません。

非常に悪意のあるTimeoutモジュールを見てきましたが、従来のインタープリターでは問題なく動作しますが、JRuby の下では機能しません。私のプログラムは、Readline ライブラリを使用する小さなコマンドライン シェルです。

TIMEOUT = 5 # seconds
loop do
  input = nil
  begin
    Timeout.timeout(TIMEOUT) do
      input = Readline::readline('> ', nil)
    end
  rescue Timeout::Error
    puts "Timeout"
    next
  end
  # do something with input
end

JRuby では、プロセスがreadline呼び出しでブロックされ、 Timeout::Error(a) タイマーが期限切れになり、(b) ユーザーが新しい行を入力した後にのみスローされるようです。そして例外は救出されません。うーん。

だから私はこの回避策を思いついた:

require 'readline'
class TimeoutException < Exception ; end
TIMEOUT = 5 # seconds

loop do
  input = nil
  start_time = Time.now
  thread = Thread.new { input = Readline::readline('> ', nil) }
  begin
    while thread.alive? do
      sleep(1) # prevent CPU from melting
      raise TimeoutException if(Time.now - start_time > TIMEOUT)
    end
  rescue TimeoutException
    thread.exit
    puts "Timeout"
  end
  # do something with input
end

これは...不格好です(礼儀正しくしましょう)。私はただ欲しいalarm(2)!このためにコア以外のライブラリ (Terminator など) をドラッグしたくありません。より良い方法はありますか?

編集:別の代替手段を取得できません-スリープしてからプロセスにシグナルを送信するスレッドを作成する-JRubyの下でも動作します。JRuby はシグナルを食べますか? 例:

SIG = 'USR2'
Signal.trap(SIG) { raise }
Process.kill(SIG, Process.pid)

JRuby は単純に戻り、Ruby は予想される「ハンドルされていない例外」エラーを返します。

4

1 に答える 1

3

プロセスに X 秒後にシグナルを送信するというより大きな問題に対する答えがなくて申し訳ありませんが、入力を待ってから X 秒後にタイムアウトするだけのようです。その場合、Kernel.select :D を探していると思います

私は個人的にこれを使用したことはありませんが、「非ブロッキング取得」についてグーグルで検索し、その後リンクを調査した後、次の2つが非常に貴重な議論であることがわかりました:

http://www.ruby-forum.com/topic/126795 (マルチスレッド get の議論)

http://www.ruby-forum.com/topic/121404 (2回目の投稿でKernel.selectの説明)

使用方法のサンプルを次に示します。これによりプロンプトが出力され、入力を待ちます... 5 秒経っても入力がない場合、プログラムは終了します。入力がある場合、入力があるとすぐに吐き出して終了します...明らかに、これは独自の目的で変更できます。

def prompt
  STDOUT.write "> "
  STDOUT.flush
end

def amusing_messages
  [ "You must enter something!", 
    "Why did you even start me if you just wanted to stare at me?", 
    "Isn't there anything better you could be doing?", 
    "Just terminate me already... this is getting old",
    "I'm waiting..."]
end

prompt

loop do
  read_array, write_array, error_array = Kernel.select [STDIN], nil, nil, 5

  if read_array.nil?
    puts amusing_messages[rand(amusing_messages.length)]
  else
    puts "Result is: #{read_array[0].read_nonblock(30)}" 
  end

  prompt 

end

おそらくあなたが望むほどエレガントではありませんが、スレッドをいじることなく仕事を成し遂げることは間違いありません。残念ながら、これはより堅牢なもの (タイマー/プロセスへのシグナルの送信) が必要な場合には役に立ちません。悲しいことに、これが JRuby で機能するかどうかはわかりません。それがそうであるかどうか知りたいです:)

于 2009-02-02T00:05:50.630 に答える