عمليات Elixir معزولة ولا تشارك أي شيء بشكل افتراضي. عندما تنهار عملية فرعية غير مرتبطة، لا تتأثر العملية الأم بها.
يمكن تغيير هذا السلوك عن طريق ربط العمليات بعضها ببعض. إذا كانت عمليتان مرتبطتين، فسيُنتقل فشل إحداهما إلى العملية الأخرى. الروابط ثنائية الاتجاه.
يمكن إنشاء العمليات مرتبطةً مسبقًا بالعملية المستدعية باستخدام spawn_link/1، وهي عملية ذرية، أو يمكن ربطها لاحقًا باستخدام Process.link/1.
قد يكون ربط العمليات مفيدًا عند القيام بعمل متوازٍ، حيث لا ينبغي متابعة أي جزء من العمل في حال فشل جزء آخر في إتمامه.
يمكن أيضًا استخدام الربط للإشراف على العمليات. إذا التقطت عملية حالات الخروج، فلن تنهار عندما تنهار عملية مرتبطة بها، بل ستتلقى بدلًا من ذلك رسالة بشأن الانهيار. وهذا يتيح لها التعامل مع الانهيار بسلاسة، على سبيل المثال بإعادة تشغيل العملية المنهارة.
يمكن ضبط عملية لالتقاط حالات الخروج عن طريق استدعاء Process.flag(:trap_exit, true). لاحظ أن Process.flag/2 تُرجع القيمة القديمة للعلامة، وليس الجديدة.
الرسالة التي ستُرسل إلى العملية في حال انهيار عملية مرتبطة بها ستطابق النمط {:EXIT, from, reason}، حيث from هو معرّف العملية (PID). وإذا كانت reason أي شيء غير الذرّة :normal، فهذا يعني أن العملية انهارت أو قُتلت بالقوة.
المهام هي عمليات مُخصصة لتنفيذ إجراء واحد محدد. وهي عادةً لا تتواصل مع العمليات الأخرى، لكنها يمكن أن تُرجع نتيجة إلى العملية التي بدأت المهمة.
تُستخدم المهام بشكل شائع لجعل العمل متوازيًا.
async/await
لبدء مهمة، استخدم Task.async/1. تأخذ دالة مجهولة كوسيط وتنفذها في عملية جديدة مرتبطة بالعملية المستدعية. وتُرجع بنية %Task{}.
للحصول على نتيجة التنفيذ، مرّر بنية %Task{} إلى Task.await/2. ستنتظر حتى تنتهي المهمة وتُرجع نتيجتها. الوسيط الثاني هو مهلة زمنية بالمللي ثانية، وقيمته الافتراضية 5000.
لاحظ أنه بين بدء المهمة وانتظارها، لا تكون العملية التي بدأت المهمة محظورة وقد تقوم بإجراءات أخرى.
يجب انتظار أي مهمة تبدأ باستخدام Task.async/1 لأنها سترسل رسالة إلى العملية المستدعية. ولا يمكن استدعاء Task.await/2 لكل مهمة إلا مرة واحدة.
start/start_link
إذا أردت بدء مهمة من أجل الآثار الجانبية فقط، فاستخدم Task.start/1 أو Task.start_link/1. ستبدأ Task.start/1 مهمة غير مرتبطة بالعملية المستدعية، وستبدأ Task.start_link/1 مهمة مرتبطة بالعملية المستدعية. وكلتا الدالتين تُرجعان زوجًا {:ok, pid}.
يستمر عملك في Instruments of Texas على آلة حاسبة RPN تجريبية. بنى فريقك بعض النماذج الأولية التي تحتاج إلى فحص شامل، لاختيار أفضل نموذج يمكن إنتاجه بكميات كبيرة.
تريد إجراء نوعين من الفحوصات.
أولًا، فحص الموثوقية الذي سيكتشف المدخلات التي تتعطل عندها الآلة الحاسبة الخاضعة للفحص أو لا تستجيب بسرعة كافية. لعزل الأعطال، يجب تشغيل الحسابات الخاصة بكل مُدخل في عملية منفصلة. يمكن استخدام الربط واعتراض رسائل الخروج في العملية المستدعية لاكتشاف ما إذا كانت العملية الحسابية قد انتهت أم تعطلت.
ثانيًا، فحص الصحة الذي سيتحقق مما إذا كانت النتيجة التي تُرجعها الآلة الحاسبة لمُدخل معين هي كما هو متوقع. الآلات الحاسبة التي اجتازت فحص الموثوقية فقط هي التي ستخضع لفحص الصحة، لذا لا داعي للقلق بشأن التعطل. ومع ذلك، ينبغي تشغيل العمليات بشكل متزامن لتسريع الإجراء، ما يجعلها حالة استخدام مثالية للمهام غير المتزامنة.
نفّذ الدالة RPNCalculatorInspection.start_reliability_check/2. ينبغي أن تأخذ وسيطين: دالة (الآلة الحاسبة) ومُدخلًا للآلة الحاسبة. وينبغي أن تُرجع خريطة تحتوي على المُدخل ومعرّف العملية (PID) للعملية المُنشأة.
ينبغي أن تستدعي العملية المُنشأة دالة الآلة الحاسبة المعطاة بالمُدخل المعطى. وينبغي أن تكون العملية مرتبطة بالعملية المستدعية.
RPNCalculatorInspection.start_reliability_check(fn _ -> 0 end, "2 3 +")
# => %{input: "2 3 +", pid: #PID<0.169.0>}
نفّذ الدالة RPNCalculatorInspection.await_reliability_check_result/2. ينبغي أن تأخذ وسيطين. الوسيط الأول خريطة تحتوي على مُدخل فحص الموثوقية ومعرّف العملية التي تُجري فحص الموثوقية لهذا المُدخل، كما تُرجعها الدالة RPNCalculatorInspection.start_reliability_check/2. والوسيط الثاني خريطة تعمل كمُجمِّع لنتائج فحوصات الموثوقية ذات المدخلات المختلفة.
ينبغي أن تنتظر الدالة رسالة خروج.
إذا استلمت رسالة خروج ({:EXIT, from, reason}) بالسبب :normal من العملية نفسها التي تُجري فحص الموثوقية، فينبغي أن تُرجع خريطة النتائج مع إضافة القيمة :ok تحت المفتاح input.
إذا استلمت رسالة خروج بسبب مختلف من العملية نفسها التي تُجري فحص الموثوقية، فينبغي أن تُرجع خريطة النتائج مع إضافة القيمة :error تحت المفتاح input.
وإذا لم تستلم أي رسائل تطابق هذه المعايير خلال 100 مللي ثانية، فينبغي أن تُرجع خريطة النتائج مع إضافة القيمة :timeout تحت المفتاح input.
# when an exit message is waiting for the process in its inbox
send(self(), {:EXIT, pid, :normal})
RPNCalculatorInspection.await_reliability_check_result(
%{input: "5 7 -", pid: pid},
%{}
)
# => %{"5 7 -" => :ok}
# when there are no messages in the process inbox
RPNCalculatorInspection.await_reliability_check_result(
%{input: "3 2 *", pid: pid},
%{"5 7 -" => :ok}
)
# => %{"5 7 -" => :ok, "3 2 *" => :timeout}
نفّذ الدالة RPNCalculatorInspection.reliability_check/2. ينبغي أن تأخذ وسيطين: دالة (الآلة الحاسبة) ومصفوفة مُدخلات للآلة الحاسبة.
لكل مُدخل في المصفوفة، ينبغي أن تبدأ فحص الموثوقية في عملية جديدة مرتبطة باستخدام start_reliability_check/2. ثم، لكل عملية بدأت بهذه الطريقة، ينبغي أن تنتظر نتائجها باستخدام await_reliability_check_result/2.
قبل بدء أي عمليات، تحتاج الدالة إلى تعيين علامة للعملية الحالية لاعتراض رسائل الخروج، حتى تتمكن من استلامها. بعد ذلك، ينبغي أن تعيد هذه العلامة إلى قيمتها الأصلية.
ينبغي أن تُرجع الدالة خريطة تحتوي على نتائج فحوصات الموثوقية لجميع المدخلات.
fake_broken_calculator = fn input ->
if String.ends_with?(input, "*"), do: raise("oops")
end
inputs = ["2 3 +", "10 3 *", "20 2 /"]
RPNCalculatorInspection.reliability_check(fake_broken_calculator, inputs)
# => %{
# "2 3 +" => :ok,
# "10 3 *" => :error,
# "20 2 /" => :ok
# }
نفّذ الدالة RPNCalculatorInspection.correctness_check/2. ينبغي أن تأخذ وسيطين: دالة (الآلة الحاسبة) ومصفوفة مُدخلات للآلة الحاسبة.
لكل مُدخل في المصفوفة، ينبغي أن تبدأ مهمة غير متزامنة تستدعي الآلة الحاسبة بالمُدخل المعطى. ثم، لكل مهمة بدأت بهذه الطريقة، ينبغي أن تنتظر نتائجها لمدة 100 مللي ثانية.
fast_cheating_calculator = fn input -> 14 end
inputs = ["13 1 +", "50 2 *", "1000 2 /"]
RPNCalculatorInspection.correctness_check(fast_cheating_calculator, inputs)
# => [14, 14, 14]
سجّل في Exercism لتتعلّم وتتقن Elixir عبر 58 مفهومًا168 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.