Неправильное использование оценка открывает Ваш код для инжекционных нападений
, Отладка может быть более сложной (никакие номера строки, и т.д.)
, код eval'd выполняется медленнее (никакая возможность компилировать/кэшировать код eval'd)
Редактирование: Как @Jeff Walden указывает в комментариях, № 3 менее верен сегодня, чем это было в 2008. Однако, в то время как некоторое кэширование скомпилированных сценариев может произойти, это будет только ограничено сценариями, которые являются eval'd, повторенным без модификации. Более вероятный сценарий - то, что Вы - eval'ing сценарии, которые подверглись небольшой модификации, каждый раз и как таковой не мог кэшироваться. Позвольте нам просто сказать, что НЕКОТОРЫЙ код eval'd выполняется более медленно.
Я обернул все решения, которые нашел на этом пути (некоторые другие проблемы, такие как выход пользователя + 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
На самом деле нам просто нужно было решить эту проблему в тестировании изоляции 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)
) в основном относятся к детали домашнего хозяйства,
Спасибо за все ответы, мое решение готово и работает, все еще нужно посмотреть, как работать с средами без разветвления, но пока оно работает :)
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
Согласно документации:
Если блок указан, этот блок выполняется в подпроцессе, и подпроцесс завершается со статусом ноль.
Итак если вы вызываете его с помощью блока, он возвращает 0. В противном случае он работает в основном так же, как системный вызов fork ()
в Unix (родительский элемент получает PID нового процесса, дочерний элемент получает ] ноль
).
Для этого можно использовать разделяемую память, если дочерний элемент должен быть лишь небольшим фрагментом кода 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 :-)
Связь вилки между двумя процессами Unix - это в основном код возврата и не более того. Однако вы можете открыть файловый дескриптор между двумя процессами и передавать данные между процессами через этот файловый дескриптор: это обычный конвейерный способ Unix.
Если вы передадите значения Marshal.dump () и Marshal.load (), вы мог легко передавать объекты Ruby между этими процессами Ruby.