রুবি ম্যাজিকের আগের সংস্করণে আমরা দেখিয়েছি কিভাবে আপনি একাধিক প্রক্রিয়া ব্যবহার করে একটি চ্যাট সিস্টেম বাস্তবায়ন করতে পারেন। এইবার আমরা আপনাকে দেখাব কিভাবে আপনি একাধিক থ্রেড ব্যবহার করে একই জিনিস করতে পারেন।
দ্রুত রিক্যাপ
আপনি যদি মৌলিক সেটআপের সম্পূর্ণ ব্যাখ্যা পেতে চান তবে পূর্ববর্তী নিবন্ধটি দেখুন। কিন্তু আপনাকে দ্রুত মনে করিয়ে দেওয়ার জন্য:আমাদের চ্যাট সিস্টেমটি এরকম দেখাচ্ছে:
আমরা একই ক্লায়েন্ট ব্যবহার করছি যা আমরা আগে ব্যবহার করেছি:
# 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-এ উপলব্ধ, তাই আপনি নিজেই এটি পরীক্ষা করতে পারেন৷
মাল্টি-থ্রেডেড চ্যাট সার্ভার
এখন আমরা সেই অংশে যাচ্ছি যেটি মাল্টি-প্রক্রিয়া বাস্তবায়নের তুলনায় ভিন্ন। মাল্টি-থ্রেডিং ব্যবহার করা হচ্ছে আমরা শুধুমাত্র একটি রুবি প্রক্রিয়ার মাধ্যমে একই সময়ে একাধিক জিনিস করতে পারি। কাজ করে এমন একাধিক থ্রেড তৈরি করে আমরা এটি করব।
থ্রেড
একটি থ্রেড স্বাধীনভাবে চলে, একটি প্রক্রিয়ার মধ্যে কোড নির্বাহ করে। একাধিক থ্রেড একই প্রক্রিয়ার মধ্যে থাকতে পারে এবং তারা মেমরি শেয়ার করতে পারে।
<img src="/images/blog/2017-04/threads.png">
আগত চ্যাট বার্তা সংরক্ষণ করার জন্য কিছু স্টোরেজ প্রয়োজন হবে। আমরা একটি প্লেইন Array
ব্যবহার করব , কিন্তু আমাদের একটি Mutex
প্রয়োজন নিশ্চিত করতে যে শুধুমাত্র একটি থ্রেড একই সময়ে বার্তাগুলি পরিবর্তন করে (আমরা দেখব কিভাবে Mutex
কিছুটা কাজ করে)।
mutex = Mutex.new
messages = []
পরবর্তীতে আমরা একটি লুপ শুরু করি যেখানে আমরা চ্যাট ক্লায়েন্টদের কাছ থেকে ইনকামিং সংযোগ গ্রহণ করব। একবার একটি সংযোগ স্থাপন হয়ে গেলে, আমরা সেই ক্লায়েন্ট সংযোগ থেকে আগত এবং বহির্গামী বার্তাগুলি পরিচালনা করার জন্য একটি থ্রেড তৈরি করব৷
Thread.new
server.accept
পর্যন্ত কল ব্লক কিছু ফেরত দেয়, এবং নতুন তৈরি থ্রেডে নিম্নলিখিত ব্লকটি দেয়। থ্রেডের কোডটি তারপরে পাঠানো প্রথম লাইনটি পড়ার জন্য এগিয়ে যায় এবং এটিকে ডাকনাম হিসাবে সংরক্ষণ করে। অবশেষে এটি বার্তা পাঠাতে এবং পড়া শুরু করে।
loop do
Thread.new(server.accept) do |socket|
nickname = read_line_from(socket)
# Send incoming message (coming up)
# Read incoming messages (coming up)
end
end
Mutex
একটি মিউটেক্স একটি বস্তু যা একাধিক থ্রেডকে সমন্বয় করতে দেয় কিভাবে তারা ভাগ করা সম্পদ ব্যবহার করে, যেমন একটি অ্যারে। একটি থ্রেড নির্দেশ করতে পারে যে এটির অ্যাক্সেস প্রয়োজন, এবং এই সময়ের মধ্যে অন্যান্য থ্রেড শেয়ার করা সম্পদ অ্যাক্সেস করতে পারে না।
সার্ভার সকেট থেকে আগত বার্তা পড়ে। এটি synchronize
ব্যবহার করে বার্তার দোকানে একটি লক পেতে, যাতে এটি নিরাপদে বার্তাগুলিতে একটি বার্তা যুক্ত করতে পারে Array
.
# Read incoming messages
while incoming = read_line_from(socket)
mutex.synchronize do
messages.push(
:time => Time.now,
:nickname => nickname,
:text => incoming
)
end
end
অবশেষে, একটি Thread
সার্ভার দ্বারা গৃহীত সমস্ত নতুন বার্তা ক্লায়েন্টকে পাঠানো হচ্ছে তা নিশ্চিত করার জন্য একটি লুপে ক্রমাগত সঞ্চালিত হয়। আবার এটি একটি লক পায় তাই এটি জানে যে অন্যান্য থ্রেডগুলি হস্তক্ষেপ করছে না। লুপের একটি টিক দিয়ে এটি সম্পন্ন করার পরে এটি কিছুক্ষণের জন্য ঘুমায় এবং তারপর চলতে থাকে৷
# Send incoming message
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
গ্লোবাল ইন্টারপ্রেটার লক
আপনি হয়তো গল্পটি শুনেছেন যে রুবি'স গ্লোবাল ইন্টারপ্রেটার লক (জিআইএল) এর কারণে রুবি "বাস্তব" থ্রেডিং করতে পারে না। এটি আংশিক সত্য। জিআইএল হল সমস্ত রুবি কোড কার্যকর করার চারপাশে একটি তালা এবং একটি রুবি প্রক্রিয়াকে একাধিক সিপিইউ একসাথে ব্যবহার করা থেকে বাধা দেয়। IO ক্রিয়াকলাপগুলি (যেমন নেটওয়ার্ক সংযোগগুলি আমরা এই নিবন্ধে ব্যবহার করেছি) GIL এর বাইরে কাজ করে, যার অর্থ এই ক্ষেত্রে আপনি প্রকৃতপক্ষে শালীন সঙ্গতি অর্জন করতে পারেন৷
সমাপ্তিতে
এখন আমাদের সংযোগ প্রতি থ্রেড ব্যবহার করে একটি একক প্রক্রিয়ার মধ্যে একটি চ্যাট সার্ভার চলছে। এটি মাল্টি-প্রক্রিয়া বাস্তবায়নের তুলনায় অনেক কম সংস্থান ব্যবহার করবে। আপনি যদি কোডারের বিশদ বিবরণ দেখতে চান তবে এটি চেষ্টা করুন আপনি এখানে উদাহরণ কোডটি খুঁজে পেতে পারেন।
এই সিরিজের চূড়ান্ত নিবন্ধে আমরা একটি একক থ্রেড এবং একটি ইভেন্ট লুপ ব্যবহার করে এই একই চ্যাটটি প্রয়োগ করব। তাত্ত্বিকভাবে এটি থ্রেড বাস্তবায়নের তুলনায় কম সম্পদ ব্যবহার করা উচিত!