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 फंक्शन लागू कीजिए। इसे 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 फंक्शन लागू कीजिए। इसे दो आर्गुमेंट लेने चाहिए। पहला आर्गुमेंट एक मैप है जिसमें विश्वसनीयता जाँच का इनपुट और उस प्रोसेस की PID होती है जो इस इनपुट के लिए विश्वसनीयता जाँच चला रहा है, जैसा कि RPNCalculatorInspection.start_reliability_check/2 लौटाता है। दूसरा आर्गुमेंट एक मैप है जो अलग-अलग इनपुट की विश्वसनीयता जाँचों के नतीजों के लिए एक्युमुलेटर का काम करता है।
इस फंक्शन को एग्ज़िट संदेश का इंतज़ार करना चाहिए।
अगर उसे उसी प्रोसेस से एग्ज़िट संदेश ({:EXIT, from, reason}) मिलता है जो विश्वसनीयता जाँच चला रहा है और उसका कारण :normal है, तो उसे नतीजों का मैप लौटाना चाहिए जिसमें input की के नीचे :ok वैल्यू जोड़ी गई हो।
अगर उसे उसी प्रोसेस से अलग कारण वाला एग्ज़िट संदेश मिलता है जो विश्वसनीयता जाँच चला रहा है, तो उसे नतीजों का मैप लौटाना चाहिए जिसमें input की के नीचे :error वैल्यू जोड़ी गई हो।
अगर उसे 100ms के अंदर इन शर्तों से मेल खाता कोई संदेश नहीं मिलता, तो उसे नतीजों का मैप लौटाना चाहिए जिसमें input की के नीचे :timeout वैल्यू जोड़ी गई हो।
# 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 फंक्शन लागू कीजिए। इसे 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 फंक्शन लागू कीजिए। इसे 2 आर्गुमेंट लेने चाहिए: एक फंक्शन (कैलकुलेटर) और कैलकुलेटर के लिए इनपुट की एक ऐरे।
ऐरे के हर इनपुट के लिए, इसे एक एसिंक्रोनस टास्क शुरू करना चाहिए जो दिए गए इनपुट के साथ कैलकुलेटर को कॉल करेगा। फिर, इस तरह शुरू की गई हर टास्क के लिए, इसे उसके नतीजों का 100ms तक इंतज़ार करना चाहिए।
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 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।