2

複数の ruby​​ プロセスで共有されている IO をロックするにはどうすればよいですか?

次のスクリプトを検討してください。

#!/usr/bin/ruby -w
# vim: ts=2 sw=2 et
if ARGV.length != 2
  $stderr.puts "Usage: test-io-fork.rb num_child num_iteration"
  exit 1
end
CHILD = ARGV[0].to_i
ITERATION = ARGV[1].to_i

def now
  t = Time.now
  "#{t.strftime('%H:%M:%S')}.#{t.usec}"
end

MAP = %w(nol satu dua tiga empat lima enam tujuh delapan sembilan)

IO.popen('-', 'w') {|pipe|
  unless pipe
    # Logger child
    File.open('test-io-fork.log', 'w') {|log|
      log.puts "#{now} Program start"
      $stdin.each {|line|
        log.puts "#{now} #{line}"
      }
      log.puts "#{now} Program end"
    }
    exit!
  end
  pipe.sync = true
  pipe.puts "Before fork"
  CHILD.times {|c|
    fork {
      pid = Process.pid
      srand
      ITERATION.times {|i|
        n = rand(9)
        sleep(n / 100000.0)
        pipe.puts "##{c}:#{i} #{MAP[n]} => #{n}, #{n} => #{MAP[n]} ##{c}:#{i}"
      }
    }
  }

}

そして、次のようにしてみてください:

./test-io-fork.rb 200 50

予想どおり、test-io-fork.log ファイルには IO 競合状態の兆候が含まれます。

私が達成したいのは、GPS ポイントをデータベースに保存するカスタム GPS プロトコル用の TCP サーバーを作成することです。このサーバーは 1000 の同時クライアントを処理するため、1000 のデータベース接続を同時に開くのではなく、データベース接続を 1 つの子のみに制限したいと考えています。このサーバーは Linux で実行されます。

4

1 に答える 1

2

アップデート

回答が受け入れられた後に更新するのは悪い形式かもしれませんが、元の形式は少し誤解を招く可能性があります。rubyが自動的に追加された改行を個別に呼び出すかどうかwrite(2)は、出力IOオブジェクトのバッファリング状態に依存します。

$stdout(ttyに接続されている場合)通常は行バッファリングされるため、(puts()適度なサイズの文字列が与えられた場合)暗黙的に改行が追加された場合の効果は、への1回の呼び出しwrite(2)です。ただし、 OPが発見したように、IO.pipeとはそうではありません。$stderr

元の回答

pipe.puts()引数を改行で終了する文字列に変更します。

pipe.puts "##{c} ... #{i}\n"  # <-- note the newline

なんで?パイプ書き込みは(おそらく)バイトpipe.sync未満であるため、アトミックでインターリーブされないことを期待して設定します。しかし、rubyのパイプ実装は、末尾の改行を追加するためにwrite(2)を個別に呼び出すPIPE_BUFため、機能しませんでした。そのため、書き込みが改行を期待した場所にインターリーブされることがあります。puts()

スクリプトのフォークに続くstraceからの裏付けとなる抜粋を次に示します。

$ strace -s 2048 -fe trace=write ./so-1326067.rb
....
4574  write(4, "#0:12 tiga => 3, 3 => tiga #0:12", 32) = 32
4574  write(4, "\n", 1)
....

ただし、独自の改行を挿入すると問題が解決し、レコード全体が1つのシステムコールで送信されるようになります。

....
5190  write(4, "#194:41 tujuh => 7, 7 => tujuh #194:41\n", 39 <unfinished ...>
5179  write(4, "#183:38 enam => 6, 6 => enam #183:38\n", 37 <unfinished ...>
....

何らかの理由でうまくいかない場合は、プロセス間ミューテックスを調整する必要があります(のようにFile.flock())。

于 2009-08-25T04:57:43.907 に答える