থ্রেডিংয়ের মাধ্যমে আরও ভালো পারফর্ম্যান্স

Android-এ থ্রেডের দক্ষ ব্যবহার আপনার অ্যাপের পারফর্ম্যান্স উন্নত করতে সাহায্য করতে পারে। এই পৃষ্ঠায় থ্রেড নিয়ে কাজ করার বিভিন্ন দিক নিয়ে আলোচনা করা হয়েছে: UI বা মূল থ্রেড নিয়ে কাজ করা; অ্যাপ লাইফসাইকেল ও থ্রেড প্রায়োরিটির মধ্যে সম্পর্ক; এবং, থ্রেড জটিলতা ম্যানেজ করতে সাহায্য করার জন্য প্ল্যাটফর্মের প্রদান করা পদ্ধতি। এইসব বিষয়ের প্রতিটিতে, এই পৃষ্ঠা সম্ভাব্য সমস্যা এবং সেগুলি এড়ানোর কৌশল বর্ণনা করে।

আসল থ্রেড

ব্যবহারকারী আপনার অ্যাপ চালু করলে, Android একটি এক্সিকিউশন থ্রেডের সাথে নতুন Linux প্রসেস তৈরি করে। এই মূল থ্রেড, যা UI থ্রেড নামেও পরিচিত, স্ক্রিনে যা কিছু ঘটে তার জন্য দায়ী। এটি কীভাবে কাজ করে তা বুঝতে পারলে, আপনি অ্যাপের ডিজাইন এমনভাবে করতে পারবেন যাতে সেরা পারফর্ম্যান্সের জন্য মূল থ্রেড ব্যবহার করা যায়।

ইন্টার্নাল

প্রধান থ্রেডের ডিজাইন খুব সহজ: এর একমাত্র কাজ হল অ্যাপ বন্ধ না হওয়া পর্যন্ত থ্রেড-সেফ ওয়ার্ক কিউ থেকে কাজের ব্লক নেওয়া এবং তা সম্পূর্ণ করা। ফ্রেমওয়ার্ক বিভিন্ন জায়গা থেকে কাজের এইসব ব্লক তৈরি করে। এইসব জায়গায় লাইফসাইকেল সংক্রান্ত তথ্য, ইনপুটের মতো ব্যবহারকারীর ইভেন্ট অথবা অন্যান্য অ্যাপ ও প্রসেস থেকে আসা ইভেন্টের সাথে যুক্ত কলব্যাক অন্তর্ভুক্ত থাকে। এছাড়াও, অ্যাপ ফ্রেমওয়ার্ক ব্যবহার না করেই স্পষ্টভাবে নিজের থেকে ব্লক এনকিউ করতে পারে।

আপনার অ্যাপের মাধ্যমে রান করা যেকোনও কোড ব্লক ইনপুট, লেআউট ইনফ্লেশন বা ড্রয়ের মতো ইভেন্ট কলব্যাকের সাথে যুক্ত থাকে। কোনও কিছু ইভেন্ট ট্রিগার করলে, যে থ্রেডে ইভেন্টটি ঘটেছে সেটি ইভেন্টটিকে নিজের থেকে বের করে মূল থ্রেডের মেসেজ সারিতে ঠেলে দেয়। তারপর মূল থ্রেড ইভেন্টটি পরিষেবা দিতে পারে।

অ্যানিমেশন বা স্ক্রিন আপডেট হওয়ার সময়, সিস্টেম প্রতি ১৬ মিলিসেকেন্ড বা তার কাছাকাছি সময়ে কাজের একটি ব্লক (যা স্ক্রিন আঁকার জন্য দায়ী) কার্যকর করার চেষ্টা করে, যাতে প্রতি সেকেন্ডে ৬০ ফ্রেম স্মুথভাবে রেন্ডার করা যায়। সিস্টেম যাতে এই লক্ষ্যে পৌঁছাতে পারে, তার জন্য UI/ভিউ হায়ারার্কি প্রধান থ্রেডে আপডেট করতে হবে। তবে, মূল থ্রেডের মেসেজিং সারিতে এমন টাস্ক থাকলে যা মূল থ্রেডের পক্ষে যথেষ্ট দ্রুত আপডেট সম্পূর্ণ করার জন্য খুব বেশি বা খুব দীর্ঘ, অ্যাপের উচিত এই কাজটি একটি ওয়ার্কার থ্রেডে সরিয়ে নেওয়া। ১৬ মিলিসেকেন্ডের মধ্যে মূল থ্রেড যদি কাজের ব্লক সম্পূর্ণ করতে না পারে, তাহলে ব্যবহারকারী ইনপুটের ক্ষেত্রে হিচিং, ল্যাগিং বা UI রেসপন্সিভনেসের অভাব লক্ষ্য করতে পারেন। মূল থ্রেড প্রায় পাঁচ সেকেন্ডের জন্য ব্লক হয়ে গেলে, সিস্টেম অ্যাপ্লিকেশন সাড়া দিচ্ছে না (ANR) ডায়ালগ দেখায়, যার মাধ্যমে ব্যবহারকারী সরাসরি অ্যাপ বন্ধ করতে পারেন।

আপনার অ্যাপে থ্রেডিং গ্রহণ করার সবচেয়ে বড় কারণ হল, মূল থ্রেড থেকে অসংখ্য বা দীর্ঘ টাস্ক সরানো, যাতে সেগুলি ব্যবহারকারীর ইনপুটের ক্ষেত্রে মসৃণ রেন্ডারিং এবং দ্রুত প্রতিক্রিয়াশীলতার সাথে হস্তক্ষেপ না করে।

থ্রেড ও UI অবজেক্ট রেফারেন্স

ডিজাইন অনুযায়ী, Android View অবজেক্ট থ্রেড-সেফ নয়। কোনও অ্যাপের থেকে আশা করা হয় যে সেটি মূল থ্রেডে UI অবজেক্ট তৈরি, ব্যবহার ও ধ্বংস করবে। আপনি যদি মূল থ্রেড ছাড়া অন্য কোনও থ্রেডে UI অবজেক্ট পরিবর্তন বা রেফারেন্স করার চেষ্টা করেন, তাহলে ফলাফল ব্যতিক্রম, সাইলেন্ট ব্যর্থতা, ক্র্যাশ এবং অন্যান্য অনির্ধারিত ভুল আচরণ হতে পারে।

রেফারেন্স সংক্রান্ত সমস্যাকে দুটি আলাদা বিভাগে ভাগ করা হয়: এক্সপ্লিসিট রেফারেন্স ও ইমপ্লিসিট রেফারেন্স।

স্পষ্ট রেফারেন্স

নন-মেন থ্রেডে অনেক টাস্কের মূল লক্ষ্য হল UI অবজেক্ট আপডেট করা। তবে, এইসব থ্রেডের মধ্যে কোনও একটি ভিউ হায়ারার্কিতে কোনও অবজেক্ট অ্যাক্সেস করলে, অ্যাপ্লিকেশনটি অস্থির হয়ে যেতে পারে: কোনও ওয়ার্কার থ্রেড যদি সেই অবজেক্টের প্রপার্টি পরিবর্তন করে, একই সময়ে অন্য কোনও থ্রেড অবজেক্টটিকে রেফারেন্স করলে, ফলাফলটি সংজ্ঞায়িত করা হয় না।

যেমন, এমন একটি অ্যাপের কথা ভাবুন যা ওয়ার্কার থ্রেডে UI অবজেক্টের সরাসরি রেফারেন্স ধরে রাখে। ওয়ার্কার থ্রেডের অবজেক্টে View-এর রেফারেন্স থাকতে পারে; তবে কাজ সম্পূর্ণ হওয়ার আগে, ভিউ হায়ারার্কি থেকে View সরিয়ে দেওয়া হয়। এই দুটি অ্যাকশন একসাথে হলে, রেফারেন্সটি মেমরিতে View অবজেক্ট রাখে এবং এতে প্রপার্টি সেট করে। তবে, ব্যবহারকারী কখনই এই অবজেক্ট দেখতে পান না এবং অবজেক্টের রেফারেন্স চলে গেলে অ্যাপটি অবজেক্টটি মুছে দেয়।

অন্য একটি উদাহরণে, Viewটি অবজেক্টে অ্যাক্টিভিটির রেফারেন্স আছে যেটি সেগুলির মালিক। যদি সেই অ্যাক্টিভিটি নষ্ট হয়ে যায়, কিন্তু কাজের একটি থ্রেডেড ব্লক থেকে যায় যা এটিকে রেফারেন্স করে—সরাসরি বা পরোক্ষভাবে—গার্বেজ কালেক্টর অ্যাক্টিভিটি সংগ্রহ করবে না যতক্ষণ না কাজের সেই ব্লকটি কার্যকর করা শেষ হয়।

এই পরিস্থিতিতে সমস্যা হতে পারে যেখানে থ্রেডেড কাজ চলতে পারে যখন কিছু অ্যাক্টিভিটি লাইফসাইকেল ইভেন্ট, যেমন স্ক্রিন রোটেশন, ঘটে। ইন-ফ্লাইট কাজ সম্পূর্ণ না হওয়া পর্যন্ত সিস্টেম গার্বেজ কালেকশন করতে পারবে না। এর ফলে, মেমরিতে দুটি Activity অবজেক্ট থাকতে পারে যতক্ষণ না পর্যন্ত গার্বেজ কালেকশন করা হচ্ছে।

এই ধরনের পরিস্থিতিতে, আমরা সাজেস্ট করি যে আপনার অ্যাপ যেন থ্রেডেড কাজের টাস্কে UI অবজেক্টের স্পষ্ট রেফারেন্স অন্তর্ভুক্ত না করে। এই ধরনের রেফারেন্স এড়িয়ে চললে আপনি এই ধরনের মেমরি লিক এড়িয়ে চলতে পারবেন এবং থ্রেডিং কনটেনশনও এড়িয়ে চলতে পারবেন।

সব ক্ষেত্রেই, আপনার অ্যাপের শুধুমাত্র মূল থ্রেডে UI অবজেক্ট আপডেট করা উচিত। এর অর্থ হল, আপনাকে এমন একটি নেগোসিয়েশন নীতি তৈরি করতে হবে যা একাধিক থ্রেডকে মূল থ্রেডে কাজ ফেরত পাঠাতে দেয়, যা প্রকৃত UI অবজেক্ট আপডেট করার কাজের সাথে সবচেয়ে উপরের অ্যাক্টিভিটি বা ফ্র্যাগমেন্টকে টাস্ক করে।

ইমপ্লিসিট রেফারেন্স

থ্রেডেড অবজেক্টের সাথে কোড-ডিজাইনের সাধারণ ত্রুটি নিচের কোড স্নিপেটে দেখা যেতে পারে:

Kotlin

class MainActivity : Activity() {
    // ...
    inner class MyAsyncTask : AsyncTask<Unit, Unit, String>() {
        override fun doInBackground(vararg params: Unit): String {...}
        override fun onPostExecute(result: String) {...}
    }
}

জাভা

public class MainActivity extends Activity {
  // ...
  public class MyAsyncTask extends AsyncTask<Void, Void, String>   {
    @Override protected String doInBackground(Void... params) {...}
    @Override protected void onPostExecute(String result) {...}
  }
}

এই স্নিপেটের ত্রুটি হল যে কোডটি থ্রেডিং অবজেক্টকে MyAsyncTask কিছু অ্যাক্টিভিটির নন-স্ট্যাটিক ইনার ক্লাস (বা Kotlin-এ ইনার ক্লাস) হিসেবে ঘোষণা করে। এই ঘোষণাটি এনক্লোজিং Activity ইনস্ট্যান্সের একটি অন্তর্নিহিত রেফারেন্স তৈরি করে। এর ফলে, থ্রেডেড কাজ সম্পূর্ণ না হওয়া পর্যন্ত অবজেক্টটিতে অ্যাক্টিভিটির একটি রেফারেন্স থাকে, যার ফলে রেফারেন্স করা অ্যাক্টিভিটি ধ্বংস হতে দেরি হয়। এই বিলম্বের ফলে মেমরির উপর আরও চাপ পড়ে।

এই সমস্যার সরাসরি সমাধান হল, আপনার ওভারলোড করা ক্লাস ইনস্ট্যান্সকে স্ট্যাটিক ক্লাস হিসেবে অথবা নিজস্ব ফাইলে সংজ্ঞায়িত করা, যাতে অন্তর্নিহিত রেফারেন্স সরিয়ে দেওয়া যায়।

অন্য একটি সমাধান হল, উপযুক্ত Activity লাইফসাইকেল কলব্যাকে, যেমন onDestroy-এ, ব্যাকগ্রাউন্ড টাস্ক সবসময় বাতিল ও ক্লিন আপ করা। তবে, এই পদ্ধতিটি ক্লান্তিকর এবং ভুল হওয়ার সম্ভাবনা থাকে। সাধারণ নিয়ম হিসেবে, অ্যাক্টিভিটিতে সরাসরি জটিল, UI-বহির্ভূত লজিক রাখবেন না। এছাড়াও, AsyncTask এখন বাতিল করা হয়েছে এবং নতুন কোডে এটি ব্যবহার করার সাজেশন দেওয়া হয় না। আপনার জন্য উপলভ্য কনকারেন্সি প্রিমিটিভ সম্পর্কে আরও জানতে Android-এ থ্রেডিং দেখুন।

থ্রেড ও অ্যাপ অ্যাক্টিভিটি লাইফসাইকেল

অ্যাপ লাইফসাইকেল আপনার অ্যাপ্লিকেশনে থ্রেডিং কীভাবে কাজ করে তা প্রভাবিত করতে পারে। কোনও অ্যাক্টিভিটি ধ্বংস হয়ে যাওয়ার পরে থ্রেডটি থাকবে কি থাকবে না, সেই সিদ্ধান্ত আপনাকে নিতে হতে পারে। এছাড়াও, থ্রেডকে অগ্রাধিকার দেওয়া এবং কোনও অ্যাক্টিভিটি ফোরগ্রাউন্ডে নাকি ব্যাকগ্রাউন্ডে চলছে, এই দুটির মধ্যে সম্পর্ক সম্পর্কে আপনাকে সচেতন হতে হবে।

থ্রেড সেভ করা

থ্রেডগুলি যে অ্যাক্টিভিটি থেকে তৈরি হয়, সেই অ্যাক্টিভিটির লাইফটাইম শেষ হয়ে যাওয়ার পরেও থ্রেডগুলি থেকে যায়। অ্যাক্টিভিটি তৈরি বা ধ্বংস করা নির্বিশেষে, থ্রেডগুলি নির্বিঘ্নে চলতে থাকে, যদিও কোনও অ্যাক্টিভ অ্যাপ্লিকেশন কম্পোনেন্ট না থাকলে অ্যাপ্লিকেশন প্রসেসের সাথে সেগুলি বন্ধ হয়ে যাবে। কিছু ক্ষেত্রে, এই অধ্যবসায় কাম্য।

এমন একটি পরিস্থিতি বিবেচনা করুন যেখানে কোনও অ্যাক্টিভিটি থ্রেডেড ওয়ার্ক ব্লকের একটি সেট তৈরি করে এবং তারপরে ওয়ার্কার থ্রেড ব্লকগুলি কার্যকর করার আগেই সেটি ধ্বংস হয়ে যায়। ফ্লাইটে থাকা ব্লকগুলির সাথে অ্যাপটির কী করা উচিত?

ব্লকগুলি যদি এমন একটি UI আপডেট করতে যাচ্ছিল যা আর নেই, তাহলে কাজ চালিয়ে যাওয়ার কোনও কারণ নেই। যেমন, যদি কোনও ডেটাবেস থেকে ব্যবহারকারীর তথ্য লোড করা এবং তারপরে ভিউ আপডেট করার কাজ হয়, তাহলে থ্রেডের আর প্রয়োজন নেই।

অন্যদিকে, ওয়ার্ক প্যাকেটের কিছু সুবিধা থাকতে পারে যা UI-এর সাথে সম্পূর্ণভাবে সম্পর্কিত নয়। এই ক্ষেত্রে, আপনাকে থ্রেডটি বজায় রাখতে হবে। যেমন, প্যাকেটগুলি কোনও ছবি ডাউনলোড, ডিস্কে ক্যাশে এবং সংশ্লিষ্ট View অবজেক্ট আপডেট করার জন্য অপেক্ষা করতে পারে। অবজেক্টটি আর না থাকলেও, ব্যবহারকারী যদি নষ্ট হয়ে যাওয়া অ্যাক্টিভিটিতে ফিরে আসেন, তাহলে ছবি ডাউনলোড ও ক্যাশে করার প্রক্রিয়াটি এখনও সহায়ক হতে পারে।

সব থ্রেডিং অবজেক্টের জন্য লাইফসাইকেল রেসপন্স ম্যানুয়ালি ম্যানেজ করা অত্যন্ত জটিল হয়ে উঠতে পারে। আপনি যদি সেগুলি সঠিকভাবে ম্যানেজ না করেন, তাহলে আপনার অ্যাপে মেমরি কন্টেনশন এবং পারফর্ম্যান্স সংক্রান্ত সমস্যা হতে পারে। LiveData-এর সাথে ViewModel কম্বাইন করলে, লাইফসাইকেল নিয়ে চিন্তা না করেই আপনি ডেটা লোড করতে এবং এটি পরিবর্তন হলে বিজ্ঞপ্তি পেতে পারবেন। ViewModel অবজেক্ট হল এই সমস্যার একটি সমাধান। কনফিগারেশন পরিবর্তন জুড়ে ViewModel বজায় রাখা হয় যা আপনার ভিউ ডেটা বজায় রাখার সহজ উপায় প্রদান করে। ViewModels সম্পর্কে আরও তথ্যের জন্য ViewModel গাইড দেখুন এবং LiveData সম্পর্কে আরও জানতে LiveData গাইড দেখুন। আপনি যদি অ্যাপ্লিকেশন আর্কিটেকচার সম্পর্কে আরও তথ্য জানতে চান, তাহলে অ্যাপ আর্কিটেকচার সংক্রান্ত নির্দেশিকা পড়ুন।

থ্রেডের অগ্রাধিকার

প্রসেস ও অ্যাপ্লিকেশন লাইফসাইকেল-এ যেমন বর্ণনা করা হয়েছে, আপনার অ্যাপের থ্রেড যে অগ্রাধিকার পায় তা আংশিকভাবে নির্ভর করে অ্যাপ লাইফসাইকেলে অ্যাপটি কোথায় আছে তার উপর। আপনার অ্যাপ্লিকেশনে থ্রেড তৈরি ও ম্যানেজ করার সময়, সেগুলির প্রায়োরিটি সেট করা গুরুত্বপূর্ণ যাতে সঠিক থ্রেড সঠিক সময়ে সঠিক প্রায়োরিটি পায়। খুব বেশি সেট করা হলে, আপনার থ্রেড UI থ্রেড এবং RenderThread-এ বাধা সৃষ্টি করতে পারে, যার ফলে আপনার অ্যাপ ফ্রেম ড্রপ করতে পারে। এটি খুব কম সেট করা হলে, আপনার অ্যাসিঙ্ক্রোনাস টাস্ক (যেমন, ছবি লোড করা) যতটা প্রয়োজন তার চেয়ে বেশি ধীরগতির হয়ে যেতে পারে।

আপনি যখনই কোনও থ্রেড তৈরি করবেন, আপনাকে setThreadPriority() কল করতে হবে। সিস্টেমের থ্রেড শিডিউলার বেশি অগ্রাধিকার থাকা থ্রেডকে অগ্রাধিকার দেয়, সেইসব অগ্রাধিকারের সাথে সব কাজ শেষ করার প্রয়োজনীয়তার মধ্যে ভারসাম্য বজায় রাখে। সাধারণত, ফোরগ্রাউন্ড গ্রুপের থ্রেডগুলি ডিভাইসের মোট এক্সিকিউশন সময়ের প্রায় ৯৫%পায়, যেখানে ব্যাকগ্রাউন্ড গ্রুপ প্রায় ৫% পায়।

এছাড়াও, সিস্টেম প্রতিটি থ্রেডের জন্য নিজস্ব প্রায়োরিটি ভ্যালু অ্যাসাইন করে, Process ক্লাস ব্যবহার করে।

ডিফল্ট হিসেবে, সিস্টেম কোনও থ্রেডের অগ্রাধিকারকে স্পনিং থ্রেডের মতো একই অগ্রাধিকার ও গ্রুপ মেম্বারশিপে সেট করে। তবে, আপনার অ্যাপ্লিকেশন setThreadPriority() ব্যবহার করে থ্রেডের অগ্রাধিকার স্পষ্টভাবে অ্যাডজাস্ট করতে পারে।

Process ক্লাসটি কনস্ট্যান্টের একটি সেট প্রদান করে প্রায়োরিটি ভ্যালু অ্যাসাইন করার জটিলতা কমাতে সাহায্য করে যা আপনার অ্যাপ থ্রেড প্রায়োরিটি সেট করতে ব্যবহার করতে পারে। যেমন, THREAD_PRIORITY_DEFAULT থ্রেডের ডিফল্ট ভ্যালু দেখায়। আপনার অ্যাপকে থ্রেডের অগ্রাধিকার THREAD_PRIORITY_BACKGROUND হিসেবে সেট করতে হবে, যে থ্রেডগুলি কম জরুরি কাজ সম্পাদন করছে।

আপনার অ্যাপ, আপেক্ষিক অগ্রাধিকার সেট করতে ইনক্রিমেন্টার হিসেবে THREAD_PRIORITY_LESS_FAVORABLE এবং THREAD_PRIORITY_MORE_FAVORABLE কনস্ট্যান্ট ব্যবহার করতে পারে। থ্রেড প্রায়োরিটির তালিকার জন্য, Process ক্লাসে THREAD_PRIORITY কনস্ট্যান্ট দেখুন।

থ্রেড ম্যানেজ করার ব্যাপারে আরও তথ্যের জন্য, Thread এবং Process ক্লাস সম্পর্কে রেফারেন্স ডকুমেন্টেশন দেখুন।

থ্রেডিংয়ের জন্য হেল্পার ক্লাস

যেসব ডেভেলপার প্রাথমিক ভাষা হিসেবে Kotlin ব্যবহার করেন, তাদের আমরা coroutines ব্যবহার করার সাজেশন দিই। Coroutines অনেক সুবিধা প্রদান করে, যার মধ্যে কলব্যাক ছাড়াই অ্যাসিঙ্ক কোড লেখার পাশাপাশি স্কোপিং, বাতিলকরণ এবং ত্রুটি পরিচালনার জন্য স্ট্রাকচার্ড কনকারেন্সি অন্তর্ভুক্ত।

এছাড়াও, ফ্রেমওয়ার্কটি থ্রেডিংয়ের সুবিধা প্রদান করার জন্য একই জাভা ক্লাস ও প্রিমিটিভ প্রদান করে, যেমন Thread, Runnable , এবং Executors ক্লাস, এর পাশাপাশি HandlerThread-এর মতো অতিরিক্ত ক্লাসও প্রদান করে। আরও তথ্যের জন্য, Android-এ থ্রেডিং দেখুন।

HandlerThread ক্লাস

হ্যান্ডলার থ্রেড হল আসলে একটি দীর্ঘ সময় ধরে চলা থ্রেড যা সারি থেকে কাজ নিয়ে সেটি সম্পন্ন করে।

আপনার Camera অবজেক্ট থেকে প্রিভিউ ফ্রেম পাওয়ার ক্ষেত্রে একটি সাধারণ চ্যালেঞ্জ বিবেচনা করুন। আপনি ক্যামেরা প্রিভিউ ফ্রেমের জন্য রেজিস্টার করলে, আপনি সেগুলি onPreviewFrame() কলব্যাকে পাবেন, যা ইভেন্ট থ্রেডে কল করা হয় যেখান থেকে এটি কল করা হয়েছিল। UI থ্রেডে এই কলব্যাক চালু করা হলে, বিশাল পিক্সেল অ্যারে নিয়ে কাজ করার টাস্কটি রেন্ডারিং ও ইভেন্ট প্রসেসিংয়ের কাজে বাধা সৃষ্টি করবে।

এই উদাহরণে, আপনার অ্যাপ যখন হ্যান্ডলার থ্রেডে কাজের ব্লকে Camera.open() কমান্ড ডেলিগেট করে, তখন সংশ্লিষ্ট onPreviewFrame() কলব্যাক UI থ্রেডের পরিবর্তে হ্যান্ডলার থ্রেডে ল্যান্ড করে। তাই, আপনি যদি পিক্সেল নিয়ে দীর্ঘ সময় ধরে কাজ করতে চান, তাহলে এটি আপনার জন্য আরও ভালো সমাধান হতে পারে।

আপনার অ্যাপ HandlerThread ব্যবহার করে থ্রেড তৈরি করলে, থ্রেডের অগ্রাধিকার সেট করতে ভুলবেন না, এটি যে ধরনের কাজ করছে তার উপর ভিত্তি করে। মনে রাখবেন, CPU একসাথে শুধুমাত্র অল্প সংখ্যক থ্রেড ম্যানেজ করতে পারে। অগ্রাধিকার সেট করলে, অন্যান্য সব থ্রেড মনোযোগ আকর্ষণ করার জন্য লড়াই করার সময়, এই কাজটি শিডিউল করার সঠিক উপায় সিস্টেম জানতে পারে।

ThreadPoolExecutor ক্লাস

কিছু নির্দিষ্ট ধরনের কাজ আছে যা অত্যন্ত সমান্তরাল, বন্টন করা টাস্কে কমিয়ে আনা যেতে পারে। যেমন, ৮ মেগাপিক্সেল ছবির প্রতিটি ৮x৮ ব্লকের জন্য ফিল্টার গণনা করা। এর ফলে তৈরি হওয়া কাজের প্যাকেটের বিপুল পরিমাণের কারণে, HandlerThread ব্যবহার করার জন্য উপযুক্ত ক্লাস নয়।

এই প্রসেস আরও সহজ করতে ThreadPoolExecutor হল একটি সহায়ক ক্লাস। এই ক্লাসটি থ্রেডের একটি গ্রুপ তৈরি করা, তাদের অগ্রাধিকার সেট করা এবং সেই থ্রেডগুলির মধ্যে কীভাবে কাজ বিতরণ করা হয় তা ম্যানেজ করে। কাজের চাপ বাড়লে বা কমলে, ক্লাসটি কাজের চাপের সাথে মানিয়ে নিতে আরও থ্রেড স্পিন আপ বা ধ্বংস করে।

এছাড়াও, এই ক্লাস আপনার অ্যাপকে সর্বাধিক সংখ্যক থ্রেড তৈরি করতে সাহায্য করে। এটি যখন ThreadPoolExecutor অবজেক্ট তৈরি করে, তখন অ্যাপটি থ্রেডের সর্বনিম্ন এবং সর্বোচ্চ সংখ্যা সেট করে। ThreadPoolExecutor-কে দেওয়া কাজের পরিমাণ বেড়ে গেলে, ক্লাসটি শুরু করা ন্যূনতম ও সর্বাধিক থ্রেড কাউন্ট বিবেচনা করবে এবং কতটা কাজ বাকি আছে তা বিবেচনা করবে। এইসব ফ্যাক্টরের উপর ভিত্তি করে, ThreadPoolExecutor সিদ্ধান্ত নেয় যে কোনও নির্দিষ্ট সময়ে কতগুলি থ্রেড চালু রাখা উচিত।

আপনি কতগুলি থ্রেড তৈরি করবেন?

সফ্টওয়্যার লেভেল থেকে আপনার কোডের শত শত থ্রেড তৈরি করার ক্ষমতা থাকলেও, এটি করলে পারফর্ম্যান্স সংক্রান্ত সমস্যা তৈরি হতে পারে। আপনার অ্যাপ ব্যাকগ্রাউন্ড পরিষেবা, রেন্ডারার, অডিও ইঞ্জিন, নেটওয়ার্কিং ও আরও অনেক কিছুর সাথে সীমিত CPU রিসোর্স শেয়ার করে। CPU-এর পক্ষে সমান্তরালভাবে অল্প সংখ্যক থ্রেড ম্যানেজ করা সম্ভব; এর থেকে বেশি কিছু হলে অগ্রাধিকার ও শিডিউল সংক্রান্ত সমস্যা দেখা দেয়। তাই, আপনার কাজের লোডের জন্য যতগুলি থ্রেড প্রয়োজন, ঠিক ততগুলিই তৈরি করা গুরুত্বপূর্ণ।

বাস্তবে, এর জন্য অনেক ভেরিয়েবল দায়ী, তবে একটি ভ্যালু বেছে নেওয়া (যেমন, ৪, স্টার্টারের জন্য) এবং Systrace-এর মাধ্যমে এটি পরীক্ষা করা অন্য যেকোনও স্ট্র্যাটেজির মতোই কার্যকরী। আপনি ট্রায়াল-অ্যান্ড-এরর পদ্ধতি ব্যবহার করে থ্রেডের ন্যূনতম সংখ্যা খুঁজে বের করতে পারেন যা কোনও সমস্যা ছাড়াই ব্যবহার করা যাবে।

কতগুলি থ্রেড থাকবে তা নির্ধারণ করার সময় আরেকটি বিষয় বিবেচনা করতে হবে যে থ্রেডগুলি ফ্রি নয়: তারা মেমরি দখল করে। প্রতিটি থ্রেডের জন্য কমপক্ষে ৬৪ হাজার মেমরি প্রয়োজন। এটি ডিভাইসে ইনস্টল করা অনেক অ্যাপ জুড়ে দ্রুত যোগ করে, বিশেষ করে এমন পরিস্থিতিতে যেখানে কল স্ট্যাক উল্লেখযোগ্যভাবে বৃদ্ধি পায়।

অনেক সিস্টেম প্রসেস এবং থার্ড-পার্টি লাইব্রেরি প্রায়শই তাদের নিজস্ব থ্রেডপুল তৈরি করে। আপনার অ্যাপ আগে থেকে থাকা থ্রেডপুল আবার ব্যবহার করতে পারলে, এই আবার ব্যবহার করার সুবিধা মেমরি ও প্রসেসিং রিসোর্সের জন্য প্রতিযোগিতা কমিয়ে পারফর্ম্যান্স উন্নত করতে সাহায্য করতে পারে।