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

  1. Неправильное использование оценка открывает Ваш код для инжекционных нападений

  2. , Отладка может быть более сложной (никакие номера строки, и т.д.)

  3. , код eval'd выполняется медленнее (никакая возможность компилировать/кэшировать код eval'd)

Редактирование: Как @Jeff Walden указывает в комментариях, № 3 менее верен сегодня, чем это было в 2008. Однако, в то время как некоторое кэширование скомпилированных сценариев может произойти, это будет только ограничено сценариями, которые являются eval'd, повторенным без модификации. Более вероятный сценарий - то, что Вы - eval'ing сценарии, которые подверглись небольшой модификации, каждый раз и как таковой не мог кэшироваться. Позвольте нам просто сказать, что НЕКОТОРЫЙ код eval'd выполняется более медленно.

24
задан 3 revs, 3 users 82% 18 May 2016 в 12:38
поделиться

6 ответов

Я обернул все решения, которые нашел на этом пути (некоторые другие проблемы, такие как выход пользователя + piping-buffers) в ruby ​​parallel gem . Теперь это так же просто, как:

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
10
ответ дан 28 November 2019 в 23:04
поделиться

На самом деле нам просто нужно было решить эту проблему в тестировании изоляции 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) ) в основном относятся к детали домашнего хозяйства,

31
ответ дан 28 November 2019 в 23:04
поделиться

Спасибо за все ответы, мое решение готово и работает, все еще нужно посмотреть, как работать с средами без разветвления, но пока оно работает :)

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

11
ответ дан 28 November 2019 в 23:04
поделиться

Согласно документации:

Если блок указан, этот блок выполняется в подпроцессе, и подпроцесс завершается со статусом ноль.

Итак если вы вызываете его с помощью блока, он возвращает 0. В противном случае он работает в основном так же, как системный вызов fork () в Unix (родительский элемент получает PID нового процесса, дочерний элемент получает ] ноль ).

0
ответ дан 28 November 2019 в 23:04
поделиться

Для этого можно использовать разделяемую память, если дочерний элемент должен быть лишь небольшим фрагментом кода Ruby. Что-то вроде следующего будет работать:

str = 'from parent'

Thread.new do
  str = 'from child'
end

sleep(1)

puts str    # outputs "from child"

Параллелизм может быть довольно сложным, и доступ к общей памяти таким образом является большой частью причины - каждый раз, когда у вас есть переменная, и другой процесс может изменить ее из-под вас , тебе следует быть очень осторожным. В качестве альтернативы вы можете использовать конвейер, который является более громоздким, но, вероятно, более безопасным для любого, кроме самого тривиального кода, а также может использоваться для запуска любой произвольной команды. Вот пример прямо из rdoc для IO.popen:

f = IO.popen("uname")
p f.readlines     # outputs "Darwin", at least on my box  :-)
0
ответ дан 28 November 2019 в 23:04
поделиться

Связь вилки между двумя процессами Unix - это в основном код возврата и не более того. Однако вы можете открыть файловый дескриптор между двумя процессами и передавать данные между процессами через этот файловый дескриптор: это обычный конвейерный способ Unix.

Если вы передадите значения Marshal.dump () и Marshal.load (), вы мог легко передавать объекты Ruby между этими процессами Ruby.

0
ответ дан 28 November 2019 в 23:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: