रूबी में थ्रेड क्या है?
थ्रेड्स आपके रूबी प्रोग्राम्स को एक ही समय में कई चीजें करने देते हैं।
जैसी चीजें:
- एकाधिक फाइलें पढ़ना
- एकाधिक वेब अनुरोधों को संभालना
- एकाधिक API कनेक्शन बनाना
थ्रेड्स का उपयोग करने के परिणामस्वरूप, आपके पास एक मल्टी-थ्रेडेड रूबी प्रोग्राम होगा, जो चीजों को तेजी से पूरा करने में सक्षम है।
लेकिन एक चेतावनी…
MRI (मैट्ज़ के रूबी इंटरप्रेटर) में, रूबी एप्लिकेशन चलाने का डिफ़ॉल्ट तरीका, आपको केवल I/O बाउंड एप्लिकेशन चलाते समय थ्रेड्स से लाभ होगा।
यह सीमा GIL (ग्लोबल इंटरप्रेटर लॉक) के कारण मौजूद है।
JRuby या Rubinius जैसे वैकल्पिक रूबी इंटरप्रेटर मल्टी-थ्रेडिंग का पूरा लाभ उठाते हैं।

तो, थ्रेड्स क्या हैं?
थ्रेड्स वर्कर्स हैं, या निष्पादन की इकाइयाँ।
हर प्रोसेस में कम से कम एक थ्रेड होता है और आप मांग पर अधिक बना सकते हैं।
मुझे पता है कि आप कोड उदाहरण देखना चाहते हैं।
लेकिन पहले, हमें CPU बाउंड और I/O बाउंड एप्लिकेशन के बीच अंतर के बारे में बात करने की आवश्यकता है।
I/O बाउंड एप्लिकेशन
एक I/O बाउंड ऐप वह है जिसे बाहरी संसाधन की प्रतीक्षा करने की आवश्यकता होती है:
- एक API अनुरोध
- डेटाबेस (क्वेरी परिणाम)
- एक डिस्क रीड
एक थ्रेड संसाधन के उपलब्ध होने की प्रतीक्षा करते समय रुकने का फैसला कर सकता है। इसका मतलब है कि दूसरा थ्रेड चल सकता है और अपना काम कर सकता है और प्रतीक्षा में समय बर्बाद नहीं करता।
I/O बाउंड ऐप का एक उदाहरण वेब क्रॉलर है।
हर अनुरोध के लिए, क्रॉलर को सर्वर की प्रतिक्रिया की प्रतीक्षा करनी पड़ती है, और यह प्रतीक्षा करते समय कुछ नहीं कर सकता।
लेकिन यदि आप थ्रेड्स का उपयोग कर रहे हैं…
आप एक समय में 4 अनुरोध कर सकते हैं और प्रतिक्रियाओं को संभाल सकते हैं जैसे वे वापस आती हैं, जिससे आप पेज तेजी से प्राप्त कर सकते हैं।
अब आपके कोड उदाहरण का समय है।
रूबी थ्रेड्स बनाना
आप Thread.new कॉल करके एक नया रूबी थ्रेड बना सकते हैं।
इस थ्रेड को चलाने के लिए आवश्यक कोड के साथ एक ब्लॉक पास करना सुनिश्चित करें।
Thread.new { puts "hello from thread" }
काफी आसान, है ना?
हालांकि।
यदि आपके पास निम्न कोड है तो आप देखेंगे कि थ्रेड से कोई आउटपुट नहीं है:
t = Thread.new { puts 10**10 }
puts "hello"
समस्या यह है कि रूबी थ्रेड्स के खत्म होने का इंतजार नहीं करता।
आपको उपरोक्त कोड को ठीक करने के लिए अपने थ्रेड पर join मेथड कॉल करने की आवश्यकता है:
t = Thread.new { puts 10**10 }
puts "hello"
t.join
यदि आप कई थ्रेड्स बनाना चाहते हैं तो आप उन्हें एक सरणी के अंदर रख सकते हैं और हर थ्रेड पर join कॉल कर सकते हैं।
उदाहरण:
threads = []
10.times { threads << Thread.new { puts 1 } }
threads.each(&:join)
रूबी थ्रेड्स की आपकी खोज के दौरान आपको डॉक्यूमेंटेशन उपयोगी लग सकता है:
https://ruby-doc.org/core-2.5.0/Thread.html
थ्रेड्स और अपवाद
यदि कोई अपवाद थ्रेड के अंदर होता है तो यह आपके प्रोग्राम को रोके बिना या किसी भी प्रकार का त्रुटि संदेश दिखाए बिना चुपचाप मर जाएगा।
यहां एक उदाहरण है:
Thread.new { raise 'hell' }
डीबगिंग उद्देश्यों के लिए, आप चाह सकते हैं कि जब कुछ बुरा हो तो आपका प्रोग्राम रुक जाए। ऐसा करने के लिए आप Thread पर निम्न फ्लैग को true पर सेट कर सकते हैं:
Thread.abort_on_exception = true
अपने थ्रेड्स बनाने से पहले इस फ्लैग को सेट करना सुनिश्चित करें 🙂
थ्रेड पूल्स
मान लीजिए आपके पास प्रोसेस करने के लिए सैकड़ों आइटम हैं, उनमें से प्रत्येक के लिए एक थ्रेड शुरू करना आपके सिस्टम संसाधनों को नष्ट कर देगा।
यह कुछ ऐसा दिखेगा:
pages_to_crawl = %w( index about contact ... )
pages_to_crawl.each do |page|
Thread.new { puts page }
end
यदि आप ऐसा करते हैं तो आप सर्वर के खिलाफ सैकड़ों कनेक्शन लॉन्च करेंगे, तो यह शायद एक अच्छा विचार नहीं है।
एक समाधान थ्रेड पूल का उपयोग करना है।
थ्रेड पूल्स आपको किसी भी समय सक्रिय थ्रेड्स की संख्या को नियंत्रित करने की अनुमति देते हैं।
आप अपना खुद का पूल बना सकते हैं, लेकिन मैं इसकी अनुशंसा नहीं करूंगा। निम्नलिखित उदाहरण में हम इसे आपके लिए करने के लिए celluloid जेम का उपयोग कर रहे हैं।
नोट: Celluloid अब अनुरक्षित नहीं है, लेकिन वर्कर पूल का सामान्य विचार अभी भी लागू होता है।
require 'celluloid'
class Worker
include Celluloid
def process_page(url)
puts url
end
end
pages_to_crawl = %w( index about contact products ... )
worker_pool = Worker.pool(size: 5)
# यदि आपको रिटर्न मान एकत्र करने की आवश्यकता है तो 'futures' देखें
pages_to_crawl.each do |page|
worker_pool.process_page(page)
end
इस बार केवल 5 थ्रेड्स चलेंगे, और जैसे ही वे समाप्त होंगे वे अगला आइटम चुन लेंगे।
रेस कंडीशन्स और अन्य खतरे
यह सब बहुत कूल लग सकता है लेकिन इससे पहले कि आप अपने कोड पर थ्रेड्स छिड़कने निकल पड़ें, आपको पता होना चाहिए कि समवर्ती कोड से जुड़ी कुछ समस्याएं हैं।
उदाहरण के लिए, थ्रेड्स रेस कंडीशन्स के शिकार होते हैं।
एक रेस कंडीशन तब होती है जब चीजें क्रम से बाहर होती हैं और गड़बड़ कर देती हैं।
एक और समस्या जो हो सकती है वह है डेडलॉक। यह तब होता है जब एक थ्रेड किसी संसाधन पर विशेष पहुंच (म्यूटेक्स जैसे लॉकिंग सिस्टम का उपयोग करके) रखता है और उसे कभी जारी नहीं करता, जो इसे अन्य सभी थ्रेड्स के लिए दुर्गम बना देता है।
इन मुद्दों से बचने के लिए, कच्चे थ्रेड्स से बचना और किसी ऐसे जेम के साथ रहना सबसे अच्छा है जो पहले से ही आपके लिए विवरण का ख्याल रखता है।
अधिक थ्रेडिंग जेम्स
हमने पहले ही अपने थ्रेड पूल के लिए celluloid का उपयोग किया, लेकिन कई अन्य concurrency-केंद्रित जेम्स हैं जिन्हें आपको देखना चाहिए:
- https://github.com/grosser/parallel
- https://github.com/chadrem/workers
- https://github.com/ruby-concurrency/concurrent-ruby
ठीक है बस इतना ही, उम्मीद है आपने रूबी थ्रेड्स के बारे में एक या दो चीजें सीखीं!
यदि आपको यह लेख उपयोगी लगा तो कृपया इसे अपने दोस्तों के साथ शेयर करें ताकि वे भी सीख सकें 🙂