रूबी डेवलपर्स के लिए समवर्ती और समांतरता पहले से कहीं अधिक महत्वपूर्ण हैं। वे हार्डवेयर का उपयोग करके हमारे अनुप्रयोगों को तेज़ बना सकते हैं जो उन्हें अपनी पूरी क्षमता में शक्ति प्रदान करते हैं। इस लेख में, हम उन टूल का पता लगाने जा रहे हैं जो वर्तमान में प्रत्येक रूबीस्ट के लिए उपलब्ध हैं और यह भी कि रूबी इस विभाग में जल्द ही क्या देने का वादा करती है।
हर कोई सीधे संगामिति का उपयोग नहीं करता है, लेकिन हम सभी इसे अप्रत्यक्ष रूप से साइडकीक जैसे उपकरणों के माध्यम से उपयोग करते हैं। रूबी संगामिति को समझना न केवल आपको अपना समाधान बनाने में मदद करेगा; यह आपको मौजूदा लोगों को समझने और उनका निवारण करने में मदद करेगा।
लेकिन पहले आइए एक कदम पीछे हटें और बड़ी तस्वीर देखें।
Concurrency vs. Parallelism
इन शब्दों का प्रयोग शिथिल रूप से किया जाता है, लेकिन इनके अलग-अलग अर्थ होते हैं।
- संगामिति: एक समय में कई कार्य करने की कला। उनके बीच जल्दी से स्विच करने से, यह उपयोगकर्ता को ऐसा प्रतीत हो सकता है जैसे वे एक साथ होते हैं।
- समानतावाद: वस्तुतः एक ही समय में कई कार्य करना। एक साथ दिखने के बजाय, वे एक साथ हैं।
Concurrency का उपयोग अक्सर उन अनुप्रयोगों के लिए किया जाता है जो IO भारी होते हैं। उदाहरण के लिए, एक वेब ऐप नियमित रूप से डेटाबेस के साथ इंटरैक्ट कर सकता है या बहुत सारे नेटवर्क अनुरोध कर सकता है। संगामिति का उपयोग करके, हम अपने आवेदन को उत्तरदायी रख सकते हैं, भले ही हम डेटाबेस के लिए हमारी क्वेरी का जवाब देने की प्रतीक्षा कर रहे हों।
यह संभव है क्योंकि रूबी वीएम अन्य थ्रेड्स को चलाने की अनुमति देता है जबकि एक आईओ के दौरान प्रतीक्षा कर रहा है। भले ही किसी प्रोग्राम को दर्जनों अनुरोध करने पड़े, अगर हम समवर्ती का उपयोग करते हैं, तो अनुरोध लगभग एक ही समय में किए जाएंगे।
दूसरी ओर, समानांतरवाद वर्तमान में रूबी द्वारा समर्थित नहीं है।
<ब्लॉकक्वॉट>रूबी में कोई समानता क्यों नहीं है?
आज, डिफ़ॉल्ट रूबी कार्यान्वयन (आमतौर पर एमआरआई या क्रुबी कहा जाता है) का उपयोग करके एकल रूबी प्रक्रिया के भीतर समानता प्राप्त करने का कोई तरीका नहीं है। रूबी वीएम एक लॉक (जीवीएम, या ग्लोबल वीएम लॉक) को लागू करता है जो एक ही समय में कई थ्रेड्स को रूबी कोड चलाने से रोकता है। यह लॉक वर्चुअल मशीन की आंतरिक स्थिति को सुरक्षित रखने के लिए और VM क्रैश होने वाले परिदृश्यों को रोकने के लिए मौजूद है। यह एक महान जगह नहीं है, लेकिन सभी आशा खोई नहीं है:रूबी 3 जल्द ही आ रहा है और यह गिल्ड नामक एक अवधारणा को पेश करके इस बाधा को हल करने का वादा करता है (इस लेख के अंतिम खंडों में समझाया गया है)।
थ्रेड्स
थ्रेड रूबी की समवर्ती वर्कहॉर्स हैं। उनका उपयोग कैसे करें और किन नुकसानों से अवगत होना चाहिए, इसे बेहतर ढंग से समझने के लिए, हम एक उदाहरण देने जा रहे हैं। हम एक छोटे से प्रोग्राम का निर्माण करेंगे जो एक एपीआई की खपत करता है और इसके परिणामों को डेटास्टोर में संगामिति का उपयोग करके संग्रहीत करता है।
एपीआई क्लाइंट बनाने से पहले, हमें एपीआई की आवश्यकता होती है। नीचे एक छोटे से एपीआई का कार्यान्वयन है जो एक संख्या को स्वीकार करता है और यदि प्रदान की गई संख्या विषम है तो सादे पाठ के रूप में प्रतिक्रिया करता है। यदि वाक्य रचना आपको अजीब लगती है, तो चिंता न करें। इसका समवर्ती से कोई लेना-देना नहीं है। यह केवल एक उपकरण है जिसका हम उपयोग करेंगे।
app =
Proc.new do |env|
sleep 0.05
qs = env['QUERY_STRING']
number = Integer(qs.match(/number=(\d+)/)[1])
[
'200',
{ 'Content-Type' => 'text/plain' },
[number.even? ? 'even' : 'odd']
]
end
run app
इस वेब ऐप को चलाने के लिए आपको रैक रत्न स्थापित करना होगा, फिर rackup config.ru
निष्पादित करें। ।
हमें एक नकली डेटास्टोर की भी आवश्यकता है। यहां एक वर्ग है जो कुंजी-मान डेटाबेस का अनुकरण करता है:
class Datastore
# ... accessors and initialization omitted ...
def read(key)
data[key]
end
def write(key, value)
data[key] = value
end
end
अब, हमारे समवर्ती समाधान के कार्यान्वयन के माध्यम से चलते हैं। हमारे पास एक तरीका है, run
, जो एक साथ 1,000 रिकॉर्ड प्राप्त करता है और उन्हें हमारे डेटास्टोर में संग्रहीत करता है।
class ThreadPoweredIntegration
# ... accessors and initialization ...
def run
threads = []
(1..1000).each_slice(250) do |subset|
threads << Thread.new do
subset.each do |number|
uri = 'https://localhost:9292/' \
"even_or_odd?number=#{number}"
status, body = AdHocHTTP.new(uri).blocking_get
handle_response(status, body)
rescue Errno::ETIMEDOUT
retry # Try again if the server times out.
end
end
end
threads.each(&:join)
end
# ...
end
हम चार धागे बनाते हैं, प्रत्येक प्रसंस्करण 250 रिकॉर्ड। हम इस रणनीति का उपयोग तृतीय-पक्ष API या अपने स्वयं के सिस्टम पर हावी न होने के लिए करते हैं।
एक साथ कई थ्रेड्स का उपयोग करके अनुरोध किए जा रहे हैं, कुल निष्पादन में अनुक्रमिक कार्यान्वयन में लगने वाले समय का एक अंश लगेगा। जबकि प्रत्येक थ्रेड में HTTP अनुरोध के माध्यम से स्थापित करने और संचार करने के लिए आवश्यक सभी चरणों के दौरान निष्क्रियता के क्षण होते हैं, रूबी वीएम एक अलग थ्रेड को चलने की अनुमति देता है। यही कारण है कि यह कार्यान्वयन क्रमिक की तुलना में बहुत तेज है।
<ब्लॉकक्वॉट>
AdHocHTTP
क्लास एक सीधा HTTP क्लाइंट है जिसे इस लेख के लिए विशेष रूप से लागू किया गया है ताकि हम केवल थ्रेड्स द्वारा संचालित कोड और फाइबर द्वारा संचालित कोड के बीच अंतर पर ध्यान केंद्रित कर सकें। इसके कार्यान्वयन पर चर्चा करना इस लेख के दायरे से बाहर है, लेकिन यदि आप उत्सुक हैं तो आप इसे यहां देख सकते हैं।
अंत में, हम आंतरिक लूप के अंत तक सर्वर की प्रतिक्रिया को संभालते हैं। यहां बताया गया है कि कैसे विधि handle_response
दिखता है:
# ... inside the ThreadPoweredIntegration class ...
attr_reader :ds
def initialize
@ds = Datastore.new(even: 0, odd: 0)
end
# ...
def handle_response(status, body)
return if status != '200'
key = body.to_sym
curr_count = ds.read(key)
ds.write(key, curr_count + 1)
end
यह तरीका ठीक दिखता है, है ना? आइए इसे चलाते हैं और देखते हैं कि हमारे डेटास्टोर पर क्या होता है:
{ even: 497, odd: 489 }
यह बहुत अजीब है, क्योंकि मुझे यकीन है कि 1 और 1000 के बीच 500 सम संख्याएं और 500 विषम संख्याएं हैं। अगले भाग में, आइए समझते हैं कि क्या हो रहा है और संक्षेप में इस बग को हल करने के तरीकों में से एक का पता लगाएं।
थ्रेड्स और डेटा रेस:द डेविल इज़ इन द डिटेल्स
थ्रेड्स का उपयोग करने से हमारे IO भारी प्रोग्राम बहुत तेज़ी से चल सकते हैं, लेकिन उन्हें ठीक करना भी कठिन होता है। ऊपर दिए गए हमारे परिणामों में त्रुटि handle_response
. में दौड़ की स्थिति के कारण होती है तरीका। एक दौड़ की स्थिति तब होती है जब दो धागे एक ही डेटा में हेरफेर करते हैं।
चूंकि हम एक साझा संसाधन पर काम कर रहे हैं (ds
डेटास्टोर ऑब्जेक्ट), हमें गैर-परमाणु संचालन के साथ विशेष रूप से सावधान रहना होगा। ध्यान दें कि हम पहले डेटास्टोर से पढ़ते हैं और--दूसरे स्टेटमेंट में--हम इसे 1 से बढ़ी हुई गिनती लिखते हैं। यह समस्याग्रस्त है क्योंकि हमारा थ्रेड पढ़ने के बाद लेकिन लिखने से पहले चलना बंद कर सकता है। फिर, यदि कोई अन्य थ्रेड चलता है और उस कुंजी के मान को बढ़ाता है जिसमें हम रुचि रखते हैं, तो हम मूल थ्रेड के फिर से शुरू होने पर एक पुरानी गणना लिखेंगे।
थ्रेड्स का उपयोग करने के खतरों को कम करने का एक तरीका समवर्ती कार्यान्वयन की संरचना के लिए उच्च-स्तरीय एब्स्ट्रैक्शन का उपयोग करना है। उपयोग करने के लिए विभिन्न पैटर्न और एक सुरक्षित थ्रेड-संचालित प्रोग्राम के लिए समवर्ती-रूबी रत्न देखें।
डेटा रेस को ठीक करने के कई तरीके हैं। एक म्यूटेक्स का उपयोग करना एक आसान समाधान है। यह सिंक्रोनाइज़ेशन तंत्र कोड के किसी दिए गए सेगमेंट में एक-एक-बार की पहुंच को लागू करता है। म्यूटेक्स के उपयोग द्वारा तय किया गया हमारा पिछला कार्यान्वयन यहां दिया गया है:
# ... inside ThreadPoweredIntegration class ...
def initialize
# ...
@semaphore = Mutex.new
end
# ...
def handle_response(status, body)
return if status != '200'
key = body.to_sym
semaphore.synchronize do
curr_count = ds.read(key)
ds.write(key, curr_count + 1)
end
end
<ब्लॉकक्वॉट> यदि आप किसी रेल एप्लिकेशन के अंदर थ्रेड्स का उपयोग करने की योजना बना रहे हैं, तो आधिकारिक मार्गदर्शिका रेल में थ्रेडिंग और कोड निष्पादन अवश्य पढ़ा जाना चाहिए। इन दिशानिर्देशों का पालन करने में विफल रहने के परिणामस्वरूप बहुत अप्रिय परिणाम हो सकते हैं, जैसे डेटाबेस कनेक्शन लीक होना।
हमारे सही कार्यान्वयन को चलाने के बाद, हमें अपेक्षित परिणाम मिलता है:
{ even: 500, odd: 500 }
म्यूटेक्स का उपयोग करने के बजाय, हम थ्रेड को पूरी तरह से छोड़कर और रूबी में उपलब्ध एक अन्य समवर्ती उपकरण तक पहुंचकर डेटा दौड़ से छुटकारा पा सकते हैं। अगले भाग में, हम IO-भारी ऐप्स के प्रदर्शन को बेहतर बनाने के लिए एक तंत्र के रूप में फाइबर पर एक नज़र डालने जा रहे हैं।
फाइबर:Concurrency के लिए एक पतला टूल
रूबी फाइबर आपको एक ही थ्रेड के भीतर सहकारी संगामिति प्राप्त करने देता है। इसका मतलब है कि फाइबर को छूट नहीं दी गई है और कार्यक्रम को ही शेड्यूलिंग करना होगा। चूंकि फाइबर शुरू और बंद होने पर प्रोग्रामर नियंत्रित करता है, इसलिए दौड़ की स्थिति से बचना बहुत आसान होता है।
थ्रेड्स के विपरीत, IO होने पर फाइबर हमें बेहतर प्रदर्शन नहीं देते हैं। सौभाग्य से, रूबी अपने IO वर्ग के माध्यम से अतुल्यकालिक पढ़ने और लिखने की सुविधा प्रदान करती है। इन async विधियों का उपयोग करके हम IO संचालन को हमारे फाइबर-आधारित कोड को अवरुद्ध करने से रोक सकते हैं।
समान परिदृश्य, अब फाइबर के साथ
आइए उसी उदाहरण के माध्यम से चलते हैं, लेकिन अब रूबी के आईओ वर्ग की एसिंक क्षमताओं के साथ संयुक्त फाइबर का उपयोग कर रहे हैं। रूबी में async IO के सभी विवरणों की व्याख्या करना इस लेख के दायरे से बाहर है। फिर भी, हम इसके कामकाज के आवश्यक हिस्सों पर ध्यान देंगे और यदि आप उत्सुक हैं तो आप AdHocHTTP के प्रासंगिक तरीकों के कार्यान्वयन पर एक नज़र डाल सकते हैं। /पी>
हम run
. को देखकर शुरू करेंगे हमारे फाइबर-संचालित कार्यान्वयन की विधि:
class FiberPoweredIntegration
# ... accessors and initialization ...
def run
(1..1000).each_slice(250) do |subset|
Fiber.new do
subset.each do |number|
uri = 'https://127.0.0.1:9292/' \
"even_or_odd?number=#{number}"
client = AdHocHTTP.new(uri)
socket = client.init_non_blocking_get
yield_if_waiting(client,
socket,
:connect_non_blocking_get)
yield_if_waiting(client,
socket,
:write_non_blocking_get)
status, body =
yield_if_waiting(client,
socket,
:read_non_blocking_get)
handle_response(status, body)
ensure
client&.close_non_blocking_get
end
end.resume
end
wait_all_requests
end
# ...
end
हम सबसे पहले संख्याओं के प्रत्येक सबसेट के लिए एक फाइबर बनाते हैं जिसे हम जांचना चाहते हैं कि क्या सम या विषम है।
फिर हम संख्याओं पर लूप करते हैं, yield_if_waiting
. पर कॉल करते हैं . यह विधि वर्तमान फाइबर को रोकने और दूसरे को फिर से शुरू करने की अनुमति देने के लिए जिम्मेदार है।
यह भी ध्यान दें कि फाइबर बनाने के बाद, हम resume
. को कॉल करते हैं . इससे फाइबर चलना शुरू हो जाता है। resume
पर कॉल करके निर्माण के तुरंत बाद, हम मुख्य लूप के 1 से 1000 खत्म होने से पहले ही HTTP अनुरोध करना शुरू कर देते हैं।
run
. के अंत में विधि, wait_all_requests
के लिए एक कॉल है . यह विधि उन तंतुओं का चयन करती है जो चलने के लिए तैयार हैं और यह भी गारंटी देता है कि हम सभी इच्छित अनुरोध करते हैं। हम इस खंड के अंतिम खंड में इस पर एक नज़र डालेंगे।
अब, देखते हैं yield_if_waiting
विस्तार से:
# ... inside FiberPoweredIntegration ...
def initialize
@ds = Datastore.new(even: 0, odd: 0)
@waiting = { wait_readable: {}, wait_writable: {} }
end
# ...
def yield_if_waiting(client, socket, operation)
res_or_status = client.send(operation)
is_waiting =
[:wait_readable,
:wait_writable].include?(res_or_status)
return res_or_status unless is_waiting
waiting[res_or_status][socket] = Fiber.current
Fiber.yield
waiting[res_or_status].delete(socket)
yield_if_waiting(client, socket, operation)
rescue Errno::ETIMEDOUT
retry # Try again if the server times out.
end
हम पहले अपने क्लाइंट का उपयोग करके एक ऑपरेशन (कनेक्ट, पढ़ना या लिखना) करने का प्रयास करते हैं। दो प्राथमिक परिणाम संभव हैं:
- सफलता: जब ऐसा होता है, हम लौट जाते हैं।
- हम एक प्रतीक प्राप्त कर सकते हैं: इसका मतलब है कि हमें इंतजार करना होगा।
कोई "इंतजार" कैसे करता है?
- हम अपने सॉकेट को वर्तमान फाइबर के साथ संयुक्त रूप से इंस्टेंस वेरिएबल
waiting
में जोड़कर एक प्रकार का चेकपॉइंट बनाते हैं (जो एकHash
है )। - हम इस जोड़ी को एक संग्रह के अंदर संग्रहीत करते हैं जिसमें आईओ पढ़ने या लिखने की प्रतीक्षा कर रहा है (हम देखेंगे कि यह एक पल में क्यों महत्वपूर्ण है), क्लाइंट से हमें वापस मिलने वाले परिणाम के आधार पर।
- हम मौजूदा फाइबर के निष्पादन को रोकते हैं, जिससे दूसरे को चलने की अनुमति मिलती है। रुके हुए फाइबर को संबद्ध नेटवर्क सॉकेट तैयार होने के बाद किसी बिंदु पर काम फिर से शुरू करने का अवसर मिलेगा। फिर, IO ऑपरेशन का पुन:प्रयास किया जाएगा (और यह समय सफल होगा)।
प्रत्येक रूबी प्रोग्राम एक फाइबर के अंदर चलता है जो स्वयं एक धागे का हिस्सा होता है (एक प्रक्रिया के अंदर सब कुछ)। नतीजतन, जब हम पहला फाइबर बनाते हैं, उसे चलाते हैं, और फिर किसी बिंदु पर उपज, हम प्रोग्राम के मध्य भाग के निष्पादन को फिर से शुरू कर रहे हैं।
अब जब हम समझते हैं कि जब फाइबर IO की प्रतीक्षा कर रहा होता है तो निष्पादन प्राप्त करने के लिए प्रयुक्त तंत्र, आइए इस फाइबर-संचालित कार्यान्वयन को समझने के लिए आवश्यक अंतिम बिट का पता लगाएं।
def wait_all_requests
while(waiting[:wait_readable].any? ||
waiting[:wait_writable].any?)
ready_to_read, ready_to_write =
IO.select(waiting[:wait_readable].keys,
waiting[:wait_writable].keys)
ready_to_read.each do |socket|
waiting[:wait_readable][socket].resume
end
ready_to_write.each do |socket|
waiting[:wait_writable][socket].resume
end
end
end
यहां मुख्य विचार सभी लंबित आईओ संचालन पूर्ण होने तक प्रतीक्षा करना (दूसरे शब्दों में, लूप करना) है।
ऐसा करने के लिए, हम IO.select
. का उपयोग करते हैं . यह लंबित IO ऑब्जेक्ट्स के दो संग्रह स्वीकार करता है:एक पढ़ने के लिए और दूसरा लिखने के लिए। यह उन IO ऑब्जेक्ट्स को लौटाता है जिन्होंने अपना काम पूरा कर लिया है। क्योंकि हमने इन IO ऑब्जेक्ट्स को चलाने के लिए ज़िम्मेदार फ़ाइबर से संबद्ध किया है, इसलिए उन फ़ाइबरों को फिर से शुरू करना आसान है।
हम इन चरणों को तब तक दोहराते रहते हैं जब तक कि सभी अनुरोध सक्रिय और पूर्ण नहीं हो जाते।
द ग्रैंड फिनाले:कम्पेरेबल परफॉर्मेंस, नो नीड फॉर लॉक्स
हमारा handle_response
विधि बिल्कुल वैसी ही है जैसी शुरू में थ्रेड्स (म्यूटेक्स के बिना संस्करण) का उपयोग करके कोड में उपयोग की जाती है। हालांकि, चूंकि हमारे सभी फाइबर एक ही थ्रेड के अंदर चलते हैं, इसलिए हमारे पास कोई डेटा रेस नहीं होगी। जब हम अपना कोड चलाते हैं, तो हमें अपेक्षित परिणाम मिलता है:
{ even: 500, odd: 500 }
<ब्लॉकक्वॉट> हर बार जब आप async IO का लाभ उठाते हैं तो आप शायद उस सभी फाइबर स्विचिंग व्यवसाय से निपटना नहीं चाहते हैं। सौभाग्य से, कुछ रत्न इस सारे काम को अमूर्त करते हैं और फाइबर के उपयोग को कुछ ऐसा बनाते हैं जिसके बारे में डेवलपर को सोचने की आवश्यकता नहीं होती है। async प्रोजेक्ट को एक अच्छी शुरुआत के रूप में देखें।
फाइबर तब चमकते हैं जब उच्च मापनीयता आवश्यक हो
यद्यपि हम छोटे पैमाने के परिदृश्यों में भी डेटा दौड़ के जोखिमों को लगभग समाप्त करने के लाभों का लाभ उठा सकते हैं, उच्च मापनीयता की आवश्यकता होने पर फाइबर एक महान उपकरण हैं। धागे की तुलना में फाइबर बहुत अधिक हल्के होते हैं। समान उपलब्ध संसाधनों को देखते हुए, थ्रेड्स बनाने से सिस्टम फाइबर की तुलना में बहुत जल्दी खत्म हो जाएगा। विषय पर एक उत्कृष्ट अन्वेषण के लिए, हम प्रस्तुति की अनुशंसा करते हैं द जर्नी टू वन मिलियन रूबी कोर टीम के सैमुअल विलियम्स द्वारा।
गिल्ड - रूबी में समानांतर प्रोग्रामिंग
अब तक हमने रूबी में समवर्ती के लिए दो उपयोगी उपकरण देखे हैं। हालांकि, उनमें से कोई भी शुद्ध गणना के प्रदर्शन में सुधार नहीं कर सकता है। इसके लिए आपको वास्तविक समानता की आवश्यकता होगी, जो वर्तमान में रूबी में मौजूद नहीं है (यहां हम एमआरआई, डिफ़ॉल्ट कार्यान्वयन पर विचार कर रहे हैं)।
यह रूबी 3 में "गिल्ड्स" नामक एक नई सुविधा के आने के साथ बदल सकता है। विवरण अभी भी अस्पष्ट हैं, लेकिन निम्नलिखित अनुभागों में हम देखेंगे कि यह कार्य-प्रगति सुविधा रूबी में समानता की अनुमति देने का वादा कैसे करती है।
गिल्ड कैसे काम कर सकते हैं
समवर्ती/समानांतर समाधानों को लागू करते समय दर्द का एक महत्वपूर्ण स्रोत साझा स्मृति है। थ्रेड्स के अनुभाग में, हमने पहले ही देखा है कि एक पर्ची बनाना और कोड लिखना कितना आसान है जो पहली नज़र में अहानिकर लग सकता है लेकिन वास्तव में सूक्ष्म बग होते हैं।
कोइची सासादा - रूबी कोर टीम के सदस्य जो नई गिल्ड फीचर के विकास का नेतृत्व कर रहे हैं - एक समाधान तैयार करने में कठिन है जो कई थ्रेड्स के बीच मेमोरी साझा करने के खतरों से निपटता है। 2018 रूबीकॉन्फ़ में अपनी प्रस्तुति में, उन्होंने बताया कि गिल्ड का उपयोग करते समय कोई भी केवल परिवर्तनशील वस्तुओं को साझा करने में सक्षम नहीं होगा। मुख्य विचार केवल अपरिवर्तनीय वस्तुओं को विभिन्न संघों के बीच साझा करने की अनुमति देकर डेटा दौड़ को रोकना है।
रुबी में विशिष्ट डेटा संरचनाएं पेश की जाएंगी ताकि गिल्डों के बीच कुछ साझा मेमोरी की अनुमति मिल सके, लेकिन यह वास्तव में कैसे काम करने वाला है, इसका विवरण अभी भी पूरी तरह से सामने नहीं आया है। एक एपीआई भी होगा जो वस्तुओं को गिल्ड के बीच कॉपी या स्थानांतरित करने की अनुमति देगा, साथ ही किसी ऑब्जेक्ट को एक अलग गिल्ड में ले जाने के बाद संदर्भित होने से रोकने के लिए एक सुरक्षा उपाय भी होगा।
एक सामान्य परिदृश्य को एक्सप्लोर करने के लिए गिल्ड का उपयोग करना
ऐसी कई स्थितियाँ हैं जहाँ आप चाहते हैं कि आप गणनाओं को समानांतर में चलाकर गति बढ़ा सकें। आइए कल्पना करें कि हमें एक ही डेटासेट के औसत और माध्य की गणना करनी है।
नीचे दिया गया उदाहरण दिखाता है कि हम इसे गिल्ड के साथ कैसे कर सकते हैं। ध्यान रखें कि यह कोड वर्तमान में काम नहीं करता है और गिल्ड जारी होने के बाद भी कभी भी काम नहीं कर सकता है।
# A frozen array of numeric values is an immutable object.
dataset = [88, 43, 37, 85, 84, 38, 13, 84, 17, 87].freeze
# The overhead of using guilds will probably be
# considerable, so it will only make sense to
# parallelize work when a dataset is large / when
# performing lots of operations.
g1 = Guild.new do
mean = dataset.reduce(:+).fdiv(dataset.length)
Guild.send_to(:mean, Guild.parent)
end
g2 = Guild.new do
median = Median.calculate(dataset.sort)
Guild.send_to(:median, Guild.parent)
end
results = {}
# Every Ruby program will be run inside a main guild;
# therefore, we can also receive messages in the main
# section of our program.
Guild.receive(:mean, :median) do |tag, result|
results[tag] = result
end
इसे सारांशित करें
संगामिति और समांतरता रूबी की मुख्य ताकत नहीं हैं, लेकिन इस विभाग में भी भाषा ऐसे उपकरण प्रदान करती है जो संभवतः अधिकांश उपयोग के मामलों से निपटने के लिए पर्याप्त हैं। रूबी 3 आ रही है और ऐसा लगता है कि गिल्ड आदिम की शुरुआत के साथ चीजें काफी बेहतर हो जाएंगी। मेरी राय में, रूबी अभी भी कई स्थितियों में एक बहुत ही उपयुक्त विकल्प है, और इसका समुदाय भाषा को और भी बेहतर बनाने के काम में स्पष्ट रूप से कठिन है। जो आ रहा है उसके लिए आइए जमीन पर ध्यान दें!