Возврат данных из разветвленных процессов

StackOverflow https://stackoverflow.com/questions/1076257

  •  21-08-2019
  •  | 
  •  

Вопрос

Если я это сделаю

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  :-)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top