Skip to main content

পাইথন প্রোগ্রামিং ল্যাঙ্গুয়েজের মাল্টি প্রসেসিং এবং মাল্টি থ্রেডিং

পাইথন প্রোগ্রামিংয়ে মাল্টি-থ্রেডিং (Multi-threading) এবং মাল্টি-প্রসেসিং (Multi-processing) হলো একই সাথে একাধিক কাজ (Concurrency) করার দুটি ভিন্ন উপায়।


১. মাল্টি-থ্রেডিং (Multi-threading)

বাস্তব উদাহরণ: আপনার রেস্টুরেন্টে একজন প্রধান শেফ (CPU/Process) আছেন এবং তার আন্ডারে ৪ জন অ্যাসিস্ট্যান্ট শেফ (Threads) আছেন। তারা সবাই একই রান্নাঘরে (Memory Space) কাজ করছেন। একজন সবজি কাটছেন, একজন মসলা পিসছেন, একজন মাংস ধুচ্ছেন। তারা একই উপাদান এবং থালা-বাসন শেয়ার করছেন।

  • কীভাবে কাজ করে: এখানে একটি মাত্র মেইন প্রোগ্রাম বা প্রসেসের ভেতরে একাধিক "থ্রেড" বা ছোট ছোট কাজের ধারা তৈরি হয়। এরা সবাই কম্পিউটারের একই মেমোরি (RAM) শেয়ার করে।

  • কোথায় ব্যবহার করবেন (I/O Bound Tasks): যখন আপনার প্রোগ্রামে এমন কাজ থাকে যেখানে CPU-কে বসে থাকতে হয় (যেমন: ইন্টারনেট থেকে ফাইল ডাউনলোড করা, ডেটাবেজ থেকে ডেটা আনা, বা ইউজারের ইনপুটের জন্য অপেক্ষা করা)।

  • পাইথনের সীমাবদ্ধতা (GIL): পাইথনে GIL (Global Interpreter Lock) নামের একটি লক থাকে। এর কারণে মাল্টি-থ্রেডিং করলেও পাইথন একসাথে মাত্র একটি থ্রেডকেই CPU ব্যবহার করতে দেয়। তাই ভারী হিসাব-নিকাশের কাজে মাল্টি-থ্রেডিং কোনো স্পিড বাড়াতে পারে না।

২. মাল্টি-প্রসেসিং (Multi-processing)

বাস্তব উদাহরণ: আপনার রেস্টুরেন্টে কাস্টমারের চাপ এত বেশি যে একজন শেফ দিয়ে আর হচ্ছে না। এবার আপনি ৪ জন আলাদা আলাদা প্রধান শেফ (Processes) নিয়োগ দিলেন এবং তাদের ৪ জনকে ৪টি আলাদা রান্নাঘর (Separate Memory/CPU Cores) দিলেন। প্রত্যেকে নিজের মতো স্বাধীনভাবে রান্না করছেন। একজনের কাজ আটকে গেলে অন্যজনের কোনো সমস্যা হচ্ছে না।

  • কীভাবে কাজ করে: এখানে আপনার কম্পিউটারের মাল্টি-কোর CPU-র সুবিধা নেওয়া হয়। প্রতিটি কাজ একদম আলাদা একটি "প্রসেস" হিসেবে রান করে এবং এদের প্রত্যেকের নিজস্ব মেমোরি স্পেস থাকে। পাইথনের GIL এখানে কোনো বাধা দিতে পারে না, কারণ প্রতিটি প্রসেসের নিজস্ব আলাদা GIL থাকে।

  • কোথায় ব্যবহার করবেন (CPU Bound Tasks): যেখানে প্রচুর গাণিতিক হিসাব-নিকাশ বা ভারী প্রসেসিংয়ের কাজ থাকে (যেমন: ইমেজ বা ভিডিও এডিটিং, বড় ডেটাসেট অ্যানালাইসিস, বা ক্রিপ্টোগ্রাফি)।

  • সীমাবদ্ধতা: যেহেতু এরা আলাদা মেমোরি ব্যবহার করে, তাই এদের তৈরি হতে কম্পিউটারের বেশি রিসোর্স (RAM ও CPU) লাগে। আবার এক প্রসেসের ডেটা অন্য প্রসেসে পাঠাতে হলে বিশেষ ব্যবস্থার (IPC - Inter-Process Communication) প্রয়োজন হয়।

প্রশ্ন: পাইথনে মাল্টি-কোর প্রসেসর থাকা সত্ত্বেও মাল্টি-থ্রেডিং কেন CPU-bound কাজের গতি বাড়াতে পারে না? তাহলে পাইথনে Core উটিলাইজেশন বাড়াতে আপনি কী করবেন?

Ans: 

পাইথনের CPython ইমপ্লিমেন্টেশনে GIL-এর কারণে একসাথে মাত্র একটি থ্রেড পাইথন বাইটকোড এক্সিকিউট করতে পারে। তাই মাল্টি-কোর থাকলেও থ্রেডগুলো আসলে এক কোর থেকে অন্য কোরে শুধু সুইচ করে (Context Switching), কিন্তু প্যারালাল চলে না।

1. Multi-processing মডিউল ব্যবহার করা (কারণ প্রতিটি প্রসেসের আলাদা GIL থাকে)।

2. ভারী কাজের জন্য C-extensions (যেমন: numpy, scipy) ব্যবহার করা, কারণ এরা ব্যাকএন্ডে GIL রিলিজ করে দেয়।

3. বিকল্প ইন্টারপ্রিটার যেমন PyPy বা Jython ব্যবহার করা।


প্রশ্ন: ২-কোর (Dual-core) বিশিষ্ট একটি মেশিনে আমি ৪টি থ্রেড একসাথে চালালাম। এটা কি Concurrency নাকি Parallelism? আর যদি ৪টি আলাদা প্রসেস চালাই, তবে সেটা কী হবে?

Ans:

৪টি থ্রেড ২-কোরে চলা মানে এটি Concurrency। কারণ কোর মাত্র ২টি, তাই যেকোনো মুহূর্তে সর্বোচ্চ ২টি থ্রেড একসাথে চলতে পারবে। বাকি ২টি থ্রেডকে ওএস টাইম-স্লাইসিং (Time-slicing) বা কনটেক্সট সুইচের মাধ্যমে এমনভাবে চালাবে যেন মনে হবে সব একসাথে চলছে।

৪টি প্রসেস ২-কোরে চলাও প্যারালালিজম নয়, এটিও কনকারেন্সি। হার্ডওয়্যার লেভেলে ট্রু Parallelism পেতে হলে আপনার টাস্ক বা প্রসেসের সংখ্যার সমান বা বেশি CPU Core থাকতে হবে।



Deadlock

প্রশ্ন: একটি ব্যাংকিং অ্যাপ্লিকেশনে দুটি অ্যাকাউন্ট থেকে একে অপরে একই সময়ে টাকা ট্রান্সফার করা হচ্ছে (Thread A: Acc 1 to Acc 2, এবং Thread B: Acc 2 to Acc 1)। আপনি ডেটা করাপশন এড়াতে Lock ব্যবহার করলেন, কিন্তু কোড ডেডলক (Deadlock) হয়ে গেল। কেন হলো এবং এটি কীভাবে ফিক্স করবেন?


Ans: 

এটাকে বলে Dynamic Order Deadlock। থ্রেড A প্রথমে Acc 1 লকিং করবে, তারপর Acc 2 লক করার চেষ্টা করবে। একই সময়ে থ্রেড B প্রথমে Acc 2 লক করবে এবং Acc 1 লক করার চেষ্টা করবে। ফলে দুজন দুজনের লক করা রিসোর্সের জন্য অনন্তকাল অপেক্ষা করবে।

Lock Ordering: রিসোর্স লকিংয়ের একটা নির্দিষ্ট অর্ডার ঠিক করে দেওয়া (যেমন: সবসময় ছোট আইডি বিশিষ্ট অ্যাকাউন্ট আগে লক হবে, অর্থাৎ Acc 1 আগে, তারপর Acc 2, ট্রান্সফারের ডিরেকশন যাই হোক না কেন)।

Mutex/Lock Timeout: নির্দিষ্ট সময় পর লক রিলিজ করে দেওয়া যাতে আনলিমিটেড সময় ধরে ডেডলক হয়ে বসে না থাকে।


প্রশ্ন: আমার একটি স্ক্র্যাপিং স্ক্রিপ্ট আছে যা ১,০০০টি ওয়েবসাইট থেকে ডেটা আনে। আমি মাল্টি-প্রসেসিং ব্যবহার করায় স্ক্রিপ্টটি অনেক স্লো হয়ে গেছে এবং সার্ভার ক্র্যাশ করছে। ভুলটা কোথায়?


Ans: 

ভুল টুল নির্বাচন। ওয়েব স্ক্র্যাপিং একটি I/O Bound কাজ (এখানে বেশি সময় কাটে নেটওয়ার্ক রেসপন্সের অপেক্ষায়, CPU-র কোনো খাটনি নেই)।

উত্তর দেওয়ার কৌশল: মাল্টি-প্রসেসিংয়ের জন্য ওএস-কে আলাদা মেমোরি স্পেস তৈরি করতে হয়, যা অত্যন্ত এক্সপেন্সিভ (High Resource Overhead)। ১,০০০টি প্রসেস তৈরি করলে র‍্যাম এবং CPU কনটেক্সট সুইচের চক্করে পড়ে সার্ভার ক্র্যাশ করবে।

সঠিক সমাধান: এখানে Multi-threading অথবা আরও আধুনিক Asyncio (Asynchronous I/O) ব্যবহার করা উচিত। এতে একটি মাত্র প্রসেসের ভেতর খুব কম রিসোর্স খরচ করে হাজার হাজার নেটওয়ার্ক রিকোয়েস্ট হ্যান্ডেল করা যায়।


প্রশ্ন: থ্রেড বা প্রসেসের সংখ্যা যত বাড়ানো যাবে, অ্যাপ্লিকেশনের পারফরম্যান্স কি ততই লিনিয়ারলি (Linear) বাড়বে? কখন থ্রেড বাড়ানো উল্টো পারফরম্যান্স কমিয়ে দেয়?


ওএস-এর Context Switching Overhead এবং Amdahl's Law সম্পর্কে আপনার ধারণা যাচাই করা।

উত্তর: না, পারফরম্যান্স লিনিয়ারলি বাড়ে না।

যখন থ্রেড বা প্রসেসের সংখ্যা CPU কোরের চেয়ে অনেক বেশি হয়ে যায়, তখন ওএস-কে এক থ্রেড থামিয়ে অন্য থ্রেডের স্টেট সেভ ও রিলোড করতে প্রচুর সময় নষ্ট করতে হয়। একে বলে Context Switching Overhead। এক পর্যায়ে আসল কাজ হওয়ার চেয়ে ওএস-এর থ্রেড ম্যানেজ করতেই CPU-র বেশি ক্ষমতা নষ্ট হয় (যাকে Thrashing-ও বলা চলে)।

এছাড়াও প্রোগ্রামের যে অংশটুকু প্যারালাল করা সম্ভব নয় (Sequential part), তার কারণে পারফরম্যান্স একটা সীমার পর আর বাড়ে না (Amdahl's Law)।


প্রশ্ন: একটি প্রোডাকশন সার্ভারে যখন হাজার হাজার ইউজার রিকোয়েস্ট আসে, তখন প্রতি রিকোয়েস্টের জন্য নতুন থ্রেড বা প্রসেস তৈরি করা কেন একটি অ্যান্টি-প্যাটার্ন (Bad Practice)? এর বিকল্প কী?


রিসোর্স ম্যানেজমেন্ট এবং ডেনিয়াল অফ সার্ভিস (DoS) প্রতিরোধ।

উত্তর: প্রতি রিকোয়েস্টে নতুন থ্রেড তৈরি করলে আনলিমিটেড রিকোয়েস্টের কারণে সার্ভারের মেমোরি শেষ হয়ে যাবে এবং সিস্টেম ক্র্যাশ করবে।

বিকল্প: Thread Pool বা Process Pool ব্যবহার করা। সিস্টেমে আগে থেকেই একটি নির্দিষ্ট সংখ্যক (যেমন: কোরের সংখ্যার ওপর ভিত্তি করে ২০ বা ৫০টি) থ্রেড তৈরি করে রাখা হয় (Worker Threads)। রিকোয়েস্ট আসলে কিউতে (Queue) জমা হয় এবং ফ্রি থ্রেডগুলো সেই কাজ সম্পন্ন করে। এতে রিসোর্স ব্যবহার সীমার মধ্যে থাকে।

Comments