Если я делаю
Process.fork do
x
end
как я могу узнать, что возвращает x (например, true/fase/string)?
(Запись в файл/базу данных не является параметром...)
Если я делаю
Process.fork do
x
end
как я могу узнать, что возвращает x (например, true/fase/string)?
(Запись в файл/базу данных не является параметром...)
Я завернул все решения, которые я нашел на этом пути (некоторые другие проблемы, такие как пользователь, выходящий из + буферов) в рубиновый параллельный жемчуг. Теперь это так же просто:
results = Parallel.map([1,2,3],:in_processes=>4) do |i|
execute_something(i)
end
или
results = Parallel.map([1,2,3],:in_threads=>4) do |i|
execute_something(i)
end
На самом деле нам просто пришлось справиться с этой проблемой в Rails изоляции тестирования. Я разместил об этом несколько в своем блоге.
В принципе, то, что вы хотите сделать, - это открыть канал в родительском и дочернем объектах и записать его в канал. Вот простой способ запустить содержимое блока в дочернем процессе и вернуть результат:
def do_in_child
read, write = IO.pipe
pid = fork do
read.close
result = yield
Marshal.dump(result, write)
exit!(0) # skips exit handlers.
end
write.close
result = read.read
Process.wait(pid)
raise "child failed" if result.empty?
Marshal.load(result)
end
Затем вы можете запустить:
do_in_child do
require "some_polluting_library"
SomePollutingLibrary.some_operation
end
Обратите внимание, что если вы требуете от ребенка, у вас не будет доступа к этой библиотеке в родительском, поэтому вы не сможете вернуть объект этого типа с помощью этого метода. Однако вы можете вернуть любой тип, доступный для обоих.
Также обратите внимание, что здесь много деталей (read.close
, Process.wait2(pid)
) - это главным образом служебные данные, поэтому, если вы используете это много, вы, вероятно, должны переместить это в библиотеку утилиты, которую вы можете повторно использовать.
Наконец, обратите внимание, что это не будет работать в Windows или JRuby, так как они не поддерживают forking.
Спасибо за все ответы, я получил свое решение и все еще нужно посмотреть, как обращаться с не-forking-средами, но на данный момент он работает:)
read, write = IO.pipe
Process.fork do
write.puts "test"
end
Process.fork do
write.puts 'test 2'
end
Process.wait
Process.wait
write.close
puts read.read
read.close
вы можете увидеть это в действии @parallel_specs Плагин Rails
Да, вы можете создать подпроцесс для выполнения блока внутри.
Я рекомендую драгоценный камень aw
:
Aw.fork! { 6 * 7 } # => 42
Конечно, он предотвращает побочные эффекты:
arr = ['foo']
Aw.fork! { arr << 'FUU' } # => ["foo", "FUU"]
arr # => ["foo"]
Согласно документации:
Если задан блок, этот блок запускается в подпроцессе, а подпроцесс завершается со статусом нуля.
Итак, если вы вызываете его с помощью блока, он возвращает 0. В противном случае он функционирует в основном так же, как системный вызов fork()
в Unix (родитель получает PID нового процесса, ребенок получает nil
).
Связь между двумя процессами Unix - это, прежде всего, код возврата и ничего больше. Тем не менее, вы можете открыть filedescriptor между двумя процессами и передать данные между процессами над этим filedescriptor: это обычный путь к Unix-каналу.
Если вы будете передавать значения Marshal.dump() и Marshal.load(), вы можете легко передать объекты Ruby между этими Ruby-процессами.
Вы можете использовать разделяемую память, чтобы сделать это, если ребенок просто должен быть небольшим фрагментом кода ruby. Будет работать следующее:
str = 'from parent'
Thread.new do
str = 'from child'
end
sleep(1)
puts str # outputs "from child"
Concurrency может быть довольно сложным, и доступ к общей памяти таким образом является большой частью причины - в любое время, когда у вас есть переменная, а другой процесс может изменить ее из-под вас, вы должны быть очень С осторожностью. Альтернативно, вы можете использовать трубу, которая является более громоздкой, но, вероятно, более безопасной для любого, но самого тривиального кода, а также может использоваться для запуска любой произвольной команды. Вот пример, прямо из rdoc для IO.popen:
f = IO.popen("uname")
p f.readlines # outputs "Darwin", at least on my box :-)