पिछले रूबी मैजिक संस्करण में हमने दिखाया कि आप कई प्रोसेस का उपयोग करके चैट सिस्टम कैसे लागू कर सकते हैं। इस बार हम आपको दिखाएंगे कि आप उसी चीज़ को कई थ्रेड का उपयोग करके कैसे कर सकते हैं।
त्वरित रिकैप
यदि आप बुनियादी सेटअप का पूरा स्पष्टीकरण चाहते हैं तो पिछले लेख को देखें। लेकिन आपको जल्दी याद दिलाने के लिए: यह है कि हमारा चैट सिस्टम कैसा दिखता है:
हम पहले उपयोग किए गए उसी क्लाइंट का उपयोग कर रहे हैं:
# client.rb
# $ ruby client.rb
require 'socket'
client = TCPSocket.open(ARGV[0], 2000)
Thread.new do
while line = client.gets
puts line.chop
end
end
while input = STDIN.gets.chomp
client.puts input
end
सर्वर के लिए बुनियादी सेटअप वही है:
# server_threads.rb
# $ ruby server_threads.rb
require 'socket'
puts 'Starting server on port 2000'
server = TCPServer.open(2000)
इस लेख में उदाहरणों में उपयोग किया गया पूरा सोर्स कोड GitHub पर उपलब्ध है, ताकि आप स्वयं इसके साथ प्रयोग कर सकें।
मल्टी-थ्रेडेड चैट सर्वर
अब हम उस भाग पर पहुंच रहे हैं जो मल्टी-प्रोसेस कार्यान्वयन की तुलना में अलग है। मल्टी-थ्रेडिंग का उपयोग करके हम केवल एक रूबी प्रोसेस के साथ एक ही समय में कई चीजें कर सकते हैं। हम इसे कई थ्रेड उत्पन्न करके करेंगे जो काम करते हैं।
थ्रेड
एक थ्रेड स्वतंत्र रूप से चलता है, एक प्रोसेस के भीतर कोड निष्पादित करता है। कई थ्रेड एक ही प्रोसेस में रह सकते हैं और वे मेमोरी साझा कर सकते हैं।

आने वाले चैट संदेशों को संग्रहीत करने के लिए कुछ स्टोरेज की आवश्यकता होगी। हम एक सादा Array उपयोग करेंगे, लेकिन हमें यह सुनिश्चित करने के लिए एक Mutex की भी आवश्यकता है कि केवल एक थ्रेड एक ही समय में संदेशों को बदलता है (हम देखेंगे कि Mutex थोड़ी देर में कैसे काम करता है)।
mutex = Mutex.new
messages = []
अब हम एक लूप शुरू करते हैं जिसमें हम चैट क्लाइंट से आने वाले कनेक्शन स्वीकार करेंगे। एक बार कनेक्शन स्थापित हो जाने पर, हम उस क्लाइंट कनेक्शन से आने वाले और जाने वाले संदेशों को संभालने के लिए एक थ्रेड उत्पन्न करेंगे।
Thread.new कॉल तब तक ब्लॉक करता है जब तक server.accept कुछ लौटाता है, और फिर नए बनाए गए थ्रेड में निम्नलिखित ब्लॉक को यील्ड करता है। थ्रेड में कोड तब भेजी गई पहली लाइन को पढ़ता है और इसे निकनेम के रूप में संग्रहीत करता है। अंत में यह संदेश भेजना और पढ़ना शुरू करता है।
loop do
Thread.new(server.accept) do |socket|
nickname = read_line_from(socket)
# आने वाला संदेश भेजें (आ रहा है)
# आने वाले संदेश पढ़ें (आ रहा है)
end
end
Mutex
म्यूटेक्स एक ऑब्जेक्ट है जो कई थ्रेड को यह समन्वय करने देता है कि वे साझा संसाधनों, जैसे सरणी, का उपयोग कैसे करते हैं। एक थ्रेड यह इंगित कर सकता है कि उसे एक्सेस की आवश्यकता है, और इस दौरान अन्य थ्रेड साझा संसाधन तक नहीं पहुंच सकते।
सर्वर सॉकेट से आने वाले संदेशों को पढ़ता है। यह synchronize का उपयोग करके संदेश स्टोर पर लॉक प्राप्त करता है, ताकि यह सुरक्षित रूप से Array में एक संदेश जोड़ सके।
# आने वाले संदेश पढ़ें
while incoming = read_line_from(socket)
mutex.synchronize do
messages.push(
:time => Time.now,
:nickname => nickname,
:text => incoming
)
end
end
अंत में, एक Thread उत्पन्न होता है जो लूप में लगातार चलता है, यह सुनिश्चित करने के लिए कि सर्वर द्वारा प्राप्त सभी नए संदेश क्लाइंट को भेजे जा रहे हैं। फिर से यह लॉक प्राप्त करता है ताकि यह जान सके कि अन्य थ्रेड हस्तक्षेप नहीं कर रहे हैं। लूप के एक टिक के बाद यह थोड़ी देर सोता है और फिर जारी रहता है।
# आने वाला संदेश भेजें
Thread.new do
sent_until = Time.now
loop do
messages_to_send = mutex.synchronize do
get_messages_to_send(nickname, messages, sent_until).tap do
sent_until = Time.now
end
end
messages_to_send.each do |message|
socket.puts "#{message[:nickname]}: #{message[:text]}"
end
sleep 0.2
end
end
ग्लोबल इंटरप्रेटर लॉक
आपने शायद कहानी सुनी होगी कि रूबी रूबी के ग्लोबल इंटरप्रेटर लॉक (GIL) के कारण "असली" थ्रेडिंग नहीं कर सकता। यह आंशिक रूप से सच है। GIL सभी रूबी कोड के निष्पादन के चारों ओर एक लॉक है और रूबी प्रोसेस को कई CPU का समवर्ती उपयोग करने से रोकता है। IO ऑपरेशन (जैसे इस लेख में उपयोग किए गए नेटवर्क कनेक्शन) GIL के बाहर काम करते हैं, जिसका अर्थ है कि आप इस मामले में वास्तव में अच्छी समवर्ती प्राप्त कर सकते हैं।
निष्कर्ष
अब हमारे पास एक चैट सर्वर है जो एक थ्रेड प्रति कनेक्शन का उपयोग करके एक ही प्रोसेस के भीतर चल रहा है। यह मल्टी-प्रोसेस कार्यान्वयन की तुलना में बहुत कम संसाधनों का उपयोग करेगा। यदि आप कोड के विवरण देखना चाहते हैं या इसे आज़माना चाहते हैं तो आप उदाहरण कोड यहां पा सकते हैं।
इस श्रृंखला के अंतिम लेख में हम इसी चैट सर्वर को एक ही थ्रेड और इवेंट लूप का उपयोग करके लागू करेंगे। सैद्धांतिक रूप से यह थ्रेड कार्यान्वयन की तुलना में और भी कम संसाधनों का उपयोग करना चाहिए!