থ্রেড বনাম প্রসেস
এই পাঠে যা শিখবেন
- থ্রেড কী এবং এটি প্রসেসের সাথে কোন রিসোর্স শেয়ার করে, আর কোনটা নিজের জন্য আলাদা রাখে
- থ্রেড কন্ট্রোল ব্লক (TCB) কীভাবে PCB-এর সাথে তুলনীয় কিন্তু ছোট
- থ্রেড তৈরি ও যোগাযোগের খরচ প্রসেসের তুলনায় কেন কম
- থ্রেডের মধ্যে সুরক্ষার অভাব কেন একটি গুরুত্বপূর্ণ, বাস্তব ট্রেড-অফ
- Python দিয়ে প্রসেস ও থ্রেডের একটি পাশাপাশি তুলনামূলক টেবিল তৈরি করা
১ · থ্রেড কী
থ্রেড (Thread)Threadএকটি প্রসেসের ভেতরের একটি হালকা এক্সিকিউশন ইউনিট, যার নিজস্ব প্রোগ্রাম কাউন্টার, রেজিস্টার সেট ও স্ট্যাক থাকে, কিন্তু প্রসেসের বাকি রিসোর্স (মেমরি, ফাইল) শেয়ার করে। হলো একটি প্রসেসের ভেতরে থাকা একটি হালকা এক্সিকিউশন ইউনিট। একই প্রসেসের একাধিক থ্রেড সেই প্রসেসের মেমরি অ্যাড্রেস স্পেস, ওপেন ফাইল এবং অন্যান্য রিসোর্স শেয়ার করে — কিন্তু প্রতিটি থ্রেডের নিজস্ব প্রোগ্রাম কাউন্টার, রেজিস্টার সেট ও স্ট্যাক থাকে। এটি সরাসরি L06-এর PCB ধারণার সাথে তুলনীয় — একটি থ্রেডেরও তার নিজস্ব ছোট্ট প্রতি-থ্রেড অবস্থা সংরক্ষণের জন্য একটি থ্রেড কন্ট্রোল ব্লক (TCB) দরকার হয়, কিন্তু প্রসেসের বাকি সবকিছু শেয়ার করা থাকে বলে TCB PCB-এর চেয়ে অনেক ছোট।
২ · থ্রেডের সুবিধা — কম খরচ
থ্রেড তৈরি করা এবং দুটি থ্রেডের মধ্যে কনটেক্সট সুইচ করা আলাদা দুটি প্রসেসের মধ্যে করার চেয়ে অনেক সস্তা — কারণ পুরো মেমরি অ্যাড্রেস স্পেস বা ওপেন-ফাইল-টেবিল সুইচ করার দরকার নেই (L06-এর কনটেক্সট সুইচিং খরচের তুলনায়), শুধু ছোট্ট থ্রেড-নির্দিষ্ট স্টেট (PC, রেজিস্টার, স্ট্যাক পয়েন্টার) সুইচ করলেই চলে। আরও একটি বড় সুবিধা — যেহেতু একই প্রসেসের থ্রেডরা এমনিতেই একই মেমরি অ্যাড্রেস স্পেস শেয়ার করে, তারা সরাসরি শেয়ার্ড ভ্যারিয়েবলের মাধ্যমে যোগাযোগ করতে পারে — L08-এর কোনো IPC সেটআপের প্রয়োজনই নেই।
৩ · থ্রেডের খরচ — সুরক্ষার অভাব
কিন্তু এই একই মেমরি-শেয়ারিং-ই থ্রেডের সবচেয়ে বড় ঝুঁকি — যেহেতু সব থ্রেড একই অ্যাড্রেস স্পেস দেখে, একটি থ্রেডের বাগ (যেমন একটি ভুল মেমরি রাইট) সহজেই একই প্রসেসের অন্য থ্রেডের ডেটা নষ্ট করে দিতে পারে। L01-এ যে সুরক্ষার কথা বলা হয়েছিল (একটি প্রসেস অন্য প্রসেসের মেমরি নষ্ট করতে পারে না) তা এখানেও প্রযোজ্য — কিন্তু সেই সুরক্ষার সীমারেখা বসে আছে প্রসেস-স্তরে, থ্রেড-স্তরে নয়। একই প্রসেসের ভেতরের থ্রেডগুলো একে অপর থেকে সুরক্ষিত নয়।
প্রসেস হলো সুরক্ষার একক (isolation unit) — খরচ বেশি কিন্তু নিরাপদ। থ্রেড হলো সমান্তরাল কাজের সস্তা একক (concurrency unit) — খরচ কম কিন্তু নিরাপত্তাহীন এক প্রসেসের ভেতরে। এই কোর্সের পরের পাঠ (L15) দেখাবে কীভাবে অপারেটিং সিস্টেম আসলে থ্রেডগুলোকে বাস্তব CPU কোরের সাথে ম্যাপ করে।
৪ · প্রসেস বনাম থ্রেড — একটি তুলনামূলক টেবিল
নিচের কোড সেলে প্রসেস ও থ্রেডকে চারটি মাপকাঠিতে পাশাপাশি তুলনা করা হয়েছে।
# প্রসেস বনাম থ্রেড -- তুলনামূলক টেবিল (toy in-memory ডেটা, কোনো real thread/process তৈরি হচ্ছে না)
comparison = {
"প্রসেস (Process)": {
"own_address_space": "হ্যাঁ -- সম্পূর্ণ আলাদা, সুরক্ষিত",
"own_open_files": "হ্যাঁ -- নিজস্ব ফাইল টেবিল",
"creation_cost": "বেশি -- নতুন অ্যাড্রেস স্পেস + ফাইল টেবিল তৈরি/কপি করতে হয়",
"communication_method": "IPC (L08) -- shared memory সেটআপ বা message passing, OS-এর সাহায্য লাগে",
},
"থ্রেড (Thread)": {
"own_address_space": "না -- একই প্রসেসের সব থ্রেড শেয়ার করে",
"own_open_files": "না -- প্রসেসের ফাইল টেবিল শেয়ার করে",
"creation_cost": "কম -- শুধু নিজস্ব স্ট্যাক, রেজিস্টার সেট, প্রোগ্রাম কাউন্টার (TCB) লাগে",
"communication_method": "সরাসরি শেয়ার্ড মেমরি -- আলাদা IPC সেটআপ লাগে না",
},
}
fields = ["own_address_space", "own_open_files", "creation_cost", "communication_method"]
labels = {
"own_address_space": "নিজস্ব অ্যাড্রেস স্পেস",
"own_open_files": "নিজস্ব ওপেন ফাইল টেবিল",
"creation_cost": "তৈরির খরচ",
"communication_method": "যোগাযোগের পদ্ধতি",
}
for field in fields:
print(f"--- {labels[field]} ---")
for entity, attrs in comparison.items():
print(f" {entity}: {attrs[field]}")
print()
থ্রেড ও প্রসেসের মধ্যে পার্থক্য মূলত একটি প্রশ্নের উত্তর — "কতটুকু শেয়ার করা হচ্ছে, আর তার বিনিময়ে কী খরচ বা ঝুঁকি নেওয়া হচ্ছে?" থ্রেড বেশি শেয়ার করে বলে সস্তা কিন্তু কম সুরক্ষিত; প্রসেস কম শেয়ার করে বলে ব্যয়বহুল কিন্তু সুরক্ষিত। বাস্তব প্রোগ্রামগুলো (যেমন একটি ওয়েব ব্রাউজার) প্রায়ই দুটোই ব্যবহার করে — একাধিক প্রসেস (সুরক্ষার জন্য, যেমন প্রতিটি ট্যাব আলাদা প্রসেস) এবং প্রতিটি প্রসেসের ভেতরে একাধিক থ্রেড (গতির জন্য)।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ যদি থ্রেডরা মেমরি অ্যাড্রেস স্পেস শেয়ার করে, তাহলে প্রতিটি থ্রেডের আলাদা স্ট্যাক থাকাটা কেন এখনও জরুরি?
একটি স্ট্যাক ফাংশন কল, লোকাল ভ্যারিয়েবল ও রিটার্ন অ্যাড্রেস ট্র্যাক করে — যদি সব থ্রেড একই স্ট্যাক শেয়ার করত, একটি থ্রেডের ফাংশন কল সরাসরি আরেকটি থ্রেডের কল-স্টেট নষ্ট করে দিত, কারণ দুটি থ্রেড একই সাথে ভিন্ন ভিন্ন কোড পাথে থাকতে পারে। তাই প্রতিটি থ্রেডের নিজস্ব স্ট্যাক দরকার যাতে সে স্বাধীনভাবে ফাংশন কল করতে পারে, যদিও সে হিপ/গ্লোবাল মেমরির মতো বাকি সবকিছু শেয়ার করে।
প্র ০২ "থ্রেড তৈরি করা সস্তা" — এই দাবিটি ঠিক কোন নির্দিষ্ট খরচ কম হওয়ার কারণে সত্যি হয়?
একটি নতুন প্রসেস তৈরি করতে OS-কে একটি নতুন মেমরি অ্যাড্রেস স্পেস বরাদ্দ করতে হয় এবং একটি নতুন ওপেন-ফাইল-টেবিল সেটআপ করতে হয় — উভয়ই সময়সাপেক্ষ। একটি নতুন থ্রেড তৈরি করতে শুধু একটি ছোট TCB (প্রোগ্রাম কাউন্টার, রেজিস্টার সেট, স্ট্যাক) বরাদ্দ করলেই চলে, কারণ মেমরি ও ফাইল টেবিল ইতিমধ্যে প্রসেস থেকে শেয়ার করা আছে — এই বাদ পড়া দুটি বড় ধাপই থ্রেড তৈরিকে দ্রুত করে তোলে।
প্র ০৩ একটি ওয়েব ব্রাউজার কেন প্রতিটি ট্যাবের জন্য আলাদা প্রসেস ব্যবহার করে, কিন্তু একটি ট্যাবের ভেতরে একাধিক থ্রেড ব্যবহার করে?
আলাদা ট্যাবকে আলাদা প্রসেস বানানো হয় সুরক্ষার জন্য — একটি ট্যাবে খারাপ/বাগযুক্ত ওয়েবসাইট ক্র্যাশ করলেও অন্য ট্যাব বা পুরো ব্রাউজার প্রভাবিত হয় না (প্রসেস-স্তরের সুরক্ষা)। কিন্তু একটি একক ট্যাবের ভেতরে (যেমন একইসাথে পেজ রেন্ডার করা, নেটওয়ার্ক রিকোয়েস্ট পাঠানো, JavaScript চালানো) একাধিক থ্রেড ব্যবহার করা হয় কারণ এই কাজগুলো একই ডেটা (একই পেজের DOM) নিয়ে কাজ করে এবং দ্রুত, সস্তা যোগাযোগ দরকার — যা থ্রেড দেয়।
অনুশীলন
-
চিন্তা করুন: আপনার ব্যবহৃত কোনো অ্যাপ্লিকেশনের কথা ভাবুন (যেমন একটি মিউজিক প্লেয়ার বা টেক্সট এডিটর) — সেটি কোথায় একাধিক থ্রেড ব্যবহার করতে পারে বলে মনে হয়?
একটি মিউজিক প্লেয়ারে সম্ভবত একটি থ্রেড অডিও ডিকোড ও প্লেব্যাক সামলায় (যাতে গান বাজা কখনও থেমে না যায়), আরেকটি থ্রেড ইউজার ইন্টারফেস (বাটন, স্লাইডার) রেসপন্সিভ রাখে, এবং সম্ভবত তৃতীয় একটি থ্রেড ব্যাকগ্রাউন্ডে গানের লাইব্রেরি স্ক্যান করে — এই কাজগুলো একই সাথে চলা দরকার এবং একই ডেটা (বর্তমান প্লেলিস্ট) শেয়ার করা দরকার বলেই থ্রেড এখানে স্বাভাবিক পছন্দ।
-
পরীক্ষা করুন: উপরের কোড সেলে
fieldsতালিকায় নতুন একটি এন্ট্রি হিসেবে"crash_isolation"যোগ করে (এবংcomparison/labels-এ যথাযথ মান দিয়ে) দেখুন টেবিলে নতুন সারি সঠিকভাবে যোগ হয় কি না।fieldsতালিকায়"crash_isolation"যোগ করলে, লুপটি স্বয়ংক্রিয়ভাবে সেই নতুন কী-এর জন্যও প্রসেস ও থ্রেড উভয়ের মান খুঁজে প্রিন্ট করবে — যতক্ষণcomparisonডিকশনারির উভয় এন্ট্রিতে (প্রসেস ও থ্রেড) সেই কী-এর একটি মান থাকে (যেমন প্রসেস: "হ্যাঁ, একটি থ্রেড ক্র্যাশ করলে পুরো প্রসেস ক্র্যাশ করে না" আর থ্রেড: "না, একই প্রসেসের অন্য থ্রেড প্রভাবিত হতে পারে"), অন্যথায় একটিKeyErrorআসবে — এটি দেখায় কোডের কাঠামো টেবিল সম্প্রসারণযোগ্য (extensible) রাখা হয়েছে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পাঠ ১৩ · মাল্টিপ্রসেসর শিডিউলিং পূর্ববর্তী পাঠ একাধিক CPU কোরের মধ্যে শিডিউলিং — থ্রেড আসলে ঠিক এই কোরগুলোতেই সিডিউল হয়।
- পাঠ ১৫ · মাল্টিথ্রেডিং মডেল পরবর্তী পাঠ এই পাঠের ইউজার-থ্রেড ধারণাটি OS-এর কার্নেল থ্রেডের সাথে ঠিক কীভাবে ম্যাপ হয় তা পরের পাঠে দেখা হবে।
- পাঠ ০৮ · ইন্টার-প্রসেস কমিউনিকেশন (IPC) সম্পর্কিত পাঠ থ্রেড কেন IPC ছাড়াই যোগাযোগ করতে পারে তা বুঝতে প্রসেসের জন্য IPC কেন দরকার হয় তা আগে জানা ভালো।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৬টি পাঠ CPU শিডিউলিং থেকে শুরু করে মেমরি, ফাইল সিস্টেম ও ভার্চুয়ালাইজেশন পর্যন্ত সম্পূর্ণ কোর্স।