2009-08-25 2 views
2

Как мы можем заблокировать IO, который был разделен несколькими процессами ruby?Как заблокировать IO, разделяемый fork в ruby ​​

Рассмотрим этот скрипт:

#!/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 

Как и ожидалось, тест-IO-fork.log файлов будет содержит знак IO состояния гонки.

Чего я хочу достичь, это сделать TCP-сервер для пользовательского протокола GPS, который сохранит точки GPS в базе данных. Поскольку этот сервер будет обрабатывать 1000 одновременных клиентов, я хотел бы ограничить подключение к базе данных только одному ребенку, вместо этого одновременно открывая 1000 соединений с базой данных. Этот сервер будет работать на Linux.

ответ

2

UPDATE

Это может быть плохая форма для обновления после того, как ответ был принят, но оригинал немного вводит в заблуждение. Независимо от того, делает ли рубин отдельный вызов write(2) для автоматически добавляемой новой строки, зависит от состояния буферизации объекта ввода-вывода.

$stdout (при подключении к TTY), как правило, линия буферизацией, так что эффект puts() - данная строка разумного размера - с неявно добавляется новая строка представляет собой единственный вызов write(2). Однако не так, с IO.pipe и $stderr, как обнаружено ОП.

ОРИГИНАЛЬНЫЙ ОТВЕТ

Изменить ваш главный аргумент pipe.puts() быть новой строки завершается строка:

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

Почему? Вы устанавливаете pipe.sync, надеясь, что запись в трубе будет атомарной и не чередующейся, поскольку они (предположительно) меньше PIPE_BUF байтов. Но это не сработало, потому что реализация ruby's puts()делает отдельный вызов для записи (2), чтобы добавить конечную новую строку,, и поэтому ваши записи иногда чередуются там, где вы ожидали новой строки.

Вот подтверждающая отрывок из вилки следования трассирования вашего сценария:

$ 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) 
.... 

Но положить в вашей собственной новой строке решает эту проблему, убедившись, что вся ваша запись передается в одном системном вызове:

.... 
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()).