মাল্টি-থ্রেডিং একটি শক্তিশালী প্রযুক্তি, যা একাধিক থ্রেডের মাধ্যমে একযোগভাবে কাজ করতে দেয় এবং পারফরম্যান্স বৃদ্ধি করতে সাহায্য করে। তবে, একাধিক থ্রেড একই ডেটা বা রিসোর্সের উপর কাজ করলে কিছু সমস্যা হতে পারে, যেগুলি প্রোগ্রামটির সঠিকতা এবং নির্ভরযোগ্যতা বিঘ্নিত করতে পারে। এই সমস্যাগুলোর মধ্যে সবচেয়ে সাধারণ সমস্যা হচ্ছে Race Condition, Deadlock, Starvation, এবং Livelock। এখানে এই সমস্যা গুলোর বিস্তারিত আলোচনা করা হবে।
Race Condition
Race Condition হল এমন একটি সমস্যা যেখানে একাধিক থ্রেড একে অপরের সাথে প্রতিযোগিতা করে, একই ডেটা বা রিসোর্সে একযোগভাবে পরিবর্তন করার জন্য। যখন একাধিক থ্রেড একসঙ্গে একটি শেয়ার করা ডেটাকে অগ্রাধিকার দিয়ে পরিবর্তন করতে চায়, তখন তাদের মধ্যে কোন থ্রেড আগে এবং কোন থ্রেড পরে কাজ করবে, এটি পূর্বানুমান করা যায় না। এই প্রতিযোগিতার ফলে অপ্রত্যাশিত বা ভুল ফলাফল আসতে পারে।
উদাহরণ
ধরা যাক, একটি ভ্যারিয়েবল counter ১০০ এ সেট করা হয়েছে, এবং দুটি থ্রেড এটি ১ করে বাড়ানোর চেষ্টা করছে:
let counter = 100;
function increment() {
for (let i = 0; i < 1000; i++) {
counter += 1; // একই সময়ে দুটি থ্রেড এই অপারেশন চালাচ্ছে
}
}
এই ক্ষেত্রে Race Condition ঘটবে কারণ দুটি থ্রেড একই সময়ে counter ভ্যারিয়েবলটি পরিবর্তন করতে পারে, ফলে প্রত্যাশিত ফলাফল (১০০০ বার বাড়ানোর পর ১১০০) আসবে না।
Deadlock
Deadlock ঘটে যখন দুটি বা ততোধিক থ্রেড একে অপরের জন্য অপেক্ষা করতে থাকে এবং কোনো থ্রেডই তার কাজ সম্পন্ন করতে পারে না। সাধারণত এটি তখন ঘটে যখন একাধিক থ্রেড একে অপরের রিসোর্স লক করে রাখে এবং একে অপরের অপেক্ষায় থাকে।
উদাহরণ
ধরা যাক, দুটি থ্রেড দুটি আলাদা রিসোর্সে লক নিয়েছে এবং তাদের একে অপরের রিসোর্সের জন্য অপেক্ষা করছে:
let lockA = false;
let lockB = false;
function thread1() {
lockA = true; // Lock A নিয়েছে
if (!lockB) {
lockB = true; // Lock B এর জন্য অপেক্ষা
}
}
function thread2() {
lockB = true; // Lock B নিয়েছে
if (!lockA) {
lockA = true; // Lock A এর জন্য অপেক্ষা
}
}
এখানে থ্রেড ১ Lock A নিয়ে Lock B এর জন্য অপেক্ষা করছে এবং থ্রেড ২ Lock B নিয়ে Lock A এর জন্য অপেক্ষা করছে, যার ফলে Deadlock সৃষ্টি হয় এবং দুটি থ্রেডই আর কাজ করতে পারে না।
Starvation
Starvation তখন ঘটে যখন একটি থ্রেড দীর্ঘ সময়ের জন্য কোনো রিসোর্স পায় না এবং অপেক্ষা করতে থাকে। এটি সাধারণত ঘটে যখন একটি থ্রেড অন্য থ্রেডের কারণে অবহেলিত হয়, যেমন যখন একাধিক থ্রেডের মধ্যে কাজের অগ্রাধিকার নির্ধারণ করা হয়, কিন্তু একটি থ্রেড সব সময় অন্য থ্রেডের দ্বারা ব্লক হয়ে থাকে।
উদাহরণ
যতটা সম্ভব কাজ করার জন্য কিছু থ্রেডকে অগ্রাধিকার দেওয়া হলে, কিছু থ্রেড একেবারেই কাজ না পেয়ে দীর্ঘ সময় ধরে অপেক্ষা করতে থাকতে পারে, যাকে Starvation বলা হয়।
Livelock
Livelock হলো একটি পরিস্থিতি যেখানে থ্রেডগুলো কাজ করতে না পারলেও ক্রমাগত একে অপরের সাথে হস্তক্ষেপ করে। এটি Deadlock এর মতো, কিন্তু এখানে থ্রেডগুলি একটি অবিরাম লুপে আটকা পড়ে, তবে তারা সম্পূর্ণভাবে থেমে থাকে না।
উদাহরণ
ধরা যাক, দুটি থ্রেড পরস্পর একে অপরের অবস্থান পরিবর্তন করতে চাচ্ছে, কিন্তু একে অপরকে ব্লক করতে থাকছে:
let flag1 = false;
let flag2 = false;
function thread1() {
while (!flag2) {
flag1 = true; // থ্রেড ১ থ্রেড ২ এর জন্য অপেক্ষা করছে
}
}
function thread2() {
while (!flag1) {
flag2 = true; // থ্রেড ২ থ্রেড ১ এর জন্য অপেক্ষা করছে
}
}
এখানে দুটি থ্রেড একে অপরের জন্য অপেক্ষা করছে এবং কাজ সম্পন্ন হচ্ছে না, যদিও তারা থেমে নেই, এটি Livelock।
সমস্যা সমাধানের কৌশল
Race Condition এর সমাধান:
- Locks: একটি থ্রেড যখন শেয়ার করা রিসোর্সে কাজ করে, তখন অন্য থ্রেডকে অপেক্ষা করানোর জন্য locks ব্যবহার করা হয়। যেমন mutexes বা semaphores।
- Atomic Operations: Atomic অপারেশন ব্যবহার করে ডেটা পরিবর্তন করা, যা থ্রেডের মধ্যে একে অপরের হস্তক্ষেপ ছাড়া সম্পন্ন হয়।
Deadlock এর সমাধান:
- Resource Ordering: রিসোর্সগুলোর একটি নির্দিষ্ট অর্ডারে অ্যাক্সেস করতে বলা, যাতে একে অপরের জন্য অপেক্ষা করতে না হয়।
- Timeouts: যদি কোনো থ্রেড একটি রিসোর্স পেতে বেশি সময় নেয়, তবে কিছু সময় পর থ্রেডটি তার কাজ বন্ধ করে দিতে পারে।
Starvation এর সমাধান:
- Fair Scheduling: প্রতিটি থ্রেডের জন্য নির্দিষ্ট সময় বরাদ্দ করা, যাতে কোনো থ্রেড অন্য থ্রেডের কারণে অবহেলিত না হয়।
Livelock এর সমাধান:
- Backoff Algorithms: থ্রেডগুলো যদি একে অপরকে ব্লক করে থাকে, তাহলে তারা নির্দিষ্ট সময়ের জন্য কাজ বন্ধ করে দেয় এবং পরবর্তীতে আবার চেষ্টা করে।
মাল্টি-থ্রেডিং অনেক সুবিধা প্রদান করলেও এতে কিছু সাধারণ সমস্যা যেমন Race Condition, Deadlock, Starvation, এবং Livelock দেখা দিতে পারে। এই সমস্যা সমাধান করতে যথাযথ synchronization এবং resource management কৌশলগুলি ব্যবহার করা হয়, যাতে সঠিক ফলাফল এবং পারফরম্যান্স নিশ্চিত করা যায়।
Read more