De retour des données de processus fourchues
Question
Si je fais
Process.fork do
x
end
comment puis-je savoir ce que x est revenu (par exemple vrai / Fase / string)?
(écriture dans un fichier / base de données ne sont pas une option ...)
La solution 3
J'enroulai toutes les solutions que j'ai trouvé le long du chemin (d'autres problèmes comme l'utilisateur sortant + tampons de tuyauterie) dans rubis petit bijou parallèle . Maintenant, il est aussi facile que:
results = Parallel.map([1,2,3],:in_processes=>4) do |i|
execute_something(i)
end
ou
results = Parallel.map([1,2,3],:in_threads=>4) do |i|
execute_something(i)
end
Autres conseils
En fait, nous avons eu à gérer ce problème dans test d'isolement Rails . Je posté à ce sujet un peu sur mon blog .
En fait, ce que vous voulez faire est d'ouvrir un tuyau dans le parent et l'enfant, et que l'enfant écrire à la conduite. Voici un moyen simple d'exécuter le contenu d'un bloc dans un processus enfant et retourner le résultat:
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
Ensuite, vous pouvez exécuter:
do_in_child do
require "some_polluting_library"
SomePollutingLibrary.some_operation
end
Notez que si vous faites un besoin chez l'enfant, vous n'aurez pas accès à cette bibliothèque dans la société mère, de sorte que vous ne pouvez pas retourner un objet de ce type en utilisant cette méthode. Cependant, vous pouvez revenir tout type qui est disponible dans les deux.
Notez également que beaucoup de détails ici (read.close
, Process.wait2(pid)
) sont la plupart du temps les détails ménage, donc si vous utilisez ce lot, vous devriez probablement passer ceci dans une bibliothèque utilitaire que vous pouvez réutiliser.
Enfin, notez que cela ne fonctionnera pas sous Windows ou JRuby, car ils ne prennent pas en charge fork.
Merci pour toutes les réponses, je me suis ma solution et en cours d'exécution, doivent encore voir comment gérer des environnements non bifurquent, mais pour l'instant il fonctionne:)
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
vous pouvez le voir en action @ parallel_specs Rails plug-in
Oui, vous pouvez créer un sous-processus pour exécuter un bloc à l'intérieur.
Je recommande le petit bijou aw
:
Aw.fork! { 6 * 7 } # => 42
Bien sûr, il empêche d'effets secondaires:
arr = ['foo']
Aw.fork! { arr << 'FUU' } # => ["foo", "FUU"]
arr # => ["foo"]
Selon la documentation:
Si un bloc est spécifié, ce bloc est exécuté dans le sous-processus, et le sous-processus se termine par un état de zéro.
Donc, si vous l'appelez avec un bloc, il renvoie 0. Sinon, il fonctionne fondamentalement les mêmes que l'appel système Unix sur fork()
(le parent reçoit le PID du nouveau processus, l'enfant reçoit nil
) .
La communication de la fourche entre deux processus Unix est principalement le code de retour et rien de plus. Cependant, vous pouvez ouvrir un descripteur de fichier entre les deux processus et transmettre les données entre les processus au cours de cette FileDescriptor. C'est la voie de conduite Unix normale
Si vous transmettre des valeurs Marshal.dump () et Marshal.load (), vous pouvez facilement passer des objets Ruby entre ces processus Ruby.
Vous pouvez utiliser la mémoire partagée pour le faire si l'enfant a juste besoin d'être un petit morceau de code Ruby. Quelque chose comme ce qui suit fonctionnera:
str = 'from parent'
Thread.new do
str = 'from child'
end
sleep(1)
puts str # outputs "from child"
Concurrency peut être assez difficile, cependant, et l'accès à la mémoire partagée de cette façon est une grande partie de la raison - chaque fois que vous avez une variable et un autre processus peut changer de sous vous, vous devriez être très prudent. Vous pouvez également utiliser un tuyau, ce qui est plus lourd, mais sans doute plus sûr pour tout mais le code le plus trivial, et peut également être utilisé pour exécuter une commande arbitraire. Voici un exemple, tout droit sorti de la rdoc pour IO.popen:
f = IO.popen("uname")
p f.readlines # outputs "Darwin", at least on my box :-)