إرجاع البيانات من العمليات متشعب
سؤال
إذا أفعل
Process.fork do
x
end
وكيف لي أن أعرف ما عاد X (على سبيل المثال الحقيقية / FASE / سلسلة)؟
و(الكتابة إلى ملف / قاعدة بيانات ليست خيارا ...)
المحلول 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
نصائح أخرى
وفعلا كان لدينا فقط للتعامل مع هذه المشكلة في <لأ href = "http://github.com/rails/rails/blob/eea7b5db1db5d7e6020c5bcb6b4d85afcbc2e696/activesupport/lib/active_support/testing/isolation.rb#L37-54" يختلط = "noreferrer"> القضبان العزلة اختبار . نشرت لي عن ذلك بعض على بلدي بلوق .
وأساسا، ما تريد القيام به هو فتح الأنابيب في الوالدين والطفل، ويكون الطفل الكتابة إلى الأنابيب. وإليك طريقة بسيطة لتشغيل محتويات كتلة في عملية طفل ونعود النتيجة:
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)
) هي تفاصيل معظمهم تنظيف الغرف، حتى إذا كنت تستخدم هذا الكثير ربما يجب عليك الانتقال من ذلك إلى مكتبة الأداة التي يمكنك إعادة استخدامها.
وأخيرا، لاحظ أن هذا لن يعمل على ويندوز أو 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 القضبان المساعد
نعم، يمكنك إنشاء فرعي أو جانبي لتنفيذ كتلة الداخل.
جوهرة aw
:
Aw.fork! { 6 * 7 } # => 42
وبطبيعة الحال، فإنه يمنع من الآثار الجانبية:
arr = ['foo']
Aw.fork! { arr << 'FUU' } # => ["foo", "FUU"]
arr # => ["foo"]
واستنادا إلى وثائق:
<اقتباس فقرة>إذا تم تحديد كتلة، يتم تشغيل تلك الكتلة في فرعي أو جانبي، وفرعي أو جانبي ينهي مع حالة الصفر.
اقتباس فقرة> وحتى إذا كنت اسميها مع كتلة، فإنها ترجع 0. وإلا، فإنه يعمل في الأساس نفس استدعاء نظام fork()
على يونيكس (الوالد يتلقى PID العملية الجديدة، والطفل يتلقى nil
).
ووالاتصالات شوكة بين عمليتين يونكس هو أساسا رمز الإرجاع وليس أكثر. ومع ذلك، يمكن فتح filedescriptor بين العمليتين وتمرير البيانات بين العمليات على هذا filedescriptor: هذا هو العادية يونكس طريق أنبوب
إذا كنت تمر Marshal.dump () وMarshal.load () القيم، يمكن بسهولة تمرير الكائنات روبي بين هذه العمليات روبي.
ويمكنك استخدام الذاكرة المشتركة أن تفعل هذا إذا كان الطفل يحتاج فقط إلى أن قطعة صغيرة من التعليمات البرمجية روبي. وشيء من هذا القبيل ما يلي يعمل:
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 :-)