Возврат данных из разветвленных процессов
Вопрос
Если я это сделаю
Process.fork do
x
end
как я могу узнать, что вернул x (напримерtrue /фаза / строка) ?
(Запись в файл / базу данных - это не вариант ...)
Решение 3
Я включил все решения, которые я нашел на этом пути (некоторые другие проблемы, такие как выход пользователя + конвейерные буферы), в рубиновый параллельный драгоценный камень.Теперь это так же просто, как:
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
Другие советы
На самом деле нам просто нужно было справиться с этой проблемой в Тестирование изоляции рельсов.Я опубликовал об этом несколько в моем блоге.
По сути, то, что вы хотите сделать, это открыть канал в родительском и дочернем, и заставить дочерний канал записывать в канал.Вот простой способ запустить содержимое блока в дочернем процессе и получить обратно результат:
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, поскольку они не поддерживают разветвление.
Спасибо за все ответы, я запустил свое решение, все еще нужно посмотреть, как обрабатывать среды без разветвления, но пока это работает :)
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
).
Обмен данными fork между двумя процессами 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"
Однако параллелизм может быть довольно сложным, и доступ к общей памяти таким способом является важной причиной - каждый раз, когда у вас есть переменная, и другой процесс может изменить ее у вас под носом, вы должны быть очень осторожны.В качестве альтернативы вы можете использовать канал, который является более громоздким, но, вероятно, более безопасным для любого, кроме самого тривиального кода, а также может использоваться для запуска любой произвольной команды.Вот пример, прямо из rdoc для ввода-вывода.popen:
f = IO.popen("uname")
p f.readlines # outputs "Darwin", at least on my box :-)