Coroutines — মূল ধারণা, Job এবং Dispatchers Kotlin-এ

লেখক: IT Sectr প্রকাশিত: 2026-03-16 পড়ার সময়: 8 মিনিট

Coroutines (করুটিন) হল Kotlin-এ অ্যাসিনক্রোনাস প্রোগ্রামিং-এর জন্য হালকা থ্রেড, যা kotlinx.coroutines লাইব্রেরির মাধ্যমে উপলব্ধ। JetBrains Kotlin Documentation, 2026 অনুসারে, Coroutines থ্রেড ব্লক না করে ফাংশন এক্সিকিউশন স্থগিত করার অনুমতি দেয়, প্রচলিত Thread-এর বিপরীতে। করুটিন সীমিত থ্রেড পুলে চলে, যা তাদের নেটিভ থ্রেডের চেয়ে হাজার গুণ হালকা করে তোলে। Kotlin Coroutines Android Jetpack, Retrofit, Room এবং Android ইকোসিস্টেমের অন্যান্য জনপ্রিয় লাইব্রেরির সাথে সম্পূর্ণরূপে সংহত।

মুখ্য বিষয়

  • Coroutines — ব্লকিং ছাড়া অ্যাসিনক্রোনাস কোডের জন্য হালকা Kotlin থ্রেড
  • suspend ফাংশন — একটি ফাংশন যা থ্রেড ব্লক না করে থামতে এবং পুনরায় শুরু করতে পারে
  • Dispatcher করুটিন এক্সিকিউশনের জন্য থ্রেড পুল নির্ধারণ করে
  • Job — বাতিলকরণ সমর্থন এবং অবস্থা ট্র্যাকিং সহ করুটিনের ডেস্ক্রিপ্টর
  • CoroutineScope করুটিনের জীবনচক্র এবং সমাপ্তির পর তাদের বাতিলকরণ পরিচালনা করে

Kotlin করুটিন কী

Coroutines হল Kotlin-এ একটি অ্যাসিনক্রোনাস প্রোগ্রামিং মেকানিজম, যা kotlinx.coroutines লাইব্রেরিতে বাস্তবায়িত। অপারেটিং সিস্টেম থ্রেডের বিপরীতে, করুটিন কোনো নির্দিষ্ট থ্রেডের সাথে আবদ্ধ নয়: তারা একটি থ্রেডে স্থগিত হয়ে অন্যটিতে পুনরায় শুরু হতে পারে। একটি একক থ্রেড ন্যূনতম ওভারহেডের সাথে হাজার হাজার করুটিন এক্সিকিউট করতে পারে।

করুটিন Kotlin 1.3 (2018) এ একটি পরীক্ষামূলক ফিচার হিসেবে আবির্ভূত হয় এবং Kotlin 1.5 (2021) এ স্থিতিশীল হয়। Coroutines async/await-এর মতো কলব্যাক হেল সমস্যা সমাধান করে, কিন্তু আরও সমৃদ্ধ API প্রদান করে: চ্যানেল (Channel), Flow, Job শ্রেণিবিন্যাসে এক্সেপশন হ্যান্ডলিং এবং Android Lifecycle-এর সাথে সরাসরি সংহতকরণ।

JetBrains (2025) অনুসারে, প্রতিটি করুটিন প্রায় 100 বাইট মেমরি খরচ করে, যেখানে নেটিভ থ্রেডের জন্য 1+ MB। এটি OutOfMemoryError-এর ঝুঁকি ছাড়া একটি অ্যাপ্লিকেশনে লক্ষ লক্ষ করুটিন চালানোর অনুমতি দেয়। করুটিনের হালকাত্বই তাদের Android-এ অ্যাসিনক্রোনিসিটির জন্য পছন্দের টুল করে তোলে।

করুটিন কীভাবে ভিতরে কাজ করে

প্রতিটি Kotlin করুটিন Continuation Passing Style (CPS)-এর মাধ্যমে একটি state machine-এ কম্পাইল হয়। কম্পাইলার প্রতিটি suspend ফাংশনে একটি লুকানো Continuation প্যারামিটার যোগ করে। Continuation-এ পুনরায় শুরু করার পয়েন্ট এবং সমস্ত লোকাল ভেরিয়েবল থাকে। যখন একটি করুটিন স্থগিত হয়, runtime Continuation সংরক্ষণ করে এবং যখন এটি পুনরায় শুরু হয়, তখন Dispatcher পুলের যেকোনো উপলব্ধ থ্রেডে এটি পুনরুদ্ধার করে।

suspend ফাংশন: স্থগিতকরণ এবং পুনরায় শুরু

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

মেকানিক্স সহজ: যখন একটি suspend ফাংশন অন্য suspend ফাংশন কল করে, এটি সেই পয়েন্টে স্থগিত হয়, থ্রেড মুক্ত করে। কলকৃত ফাংশন সম্পূর্ণ হওয়ার পর, runtime সংরক্ষিত অবস্থান থেকে এক্সিকিউশন চালিয়ে যায়। এটিকে cooperative cancellation বলা হয় — কোনো থ্রেড ব্লক হয় না।

  • স্থগিতকরণ — করুটিন থ্রেড ব্লক না করে মুক্ত করে
  • পুনরায় শুরু — করুটিন যেখানে স্থগিত হয়েছিল সেখান থেকে চালিয়ে যায়
  • থ্রেড — করুটিন থ্রেড A-তে স্থগিত হয়ে থ্রেড B-তে পুনরায় শুরু হতে পারে
  • এক্সেপশন — সিনক্রোনাস কোডের মতো try/catch-এর মাধ্যমে হ্যান্ডল করা হয়

গুরুত্বপূর্ণ: suspend ফাংশন ডিফল্টরূপে অ্যাসিনক্রোনাস নয়। launch বা async ব্যবহার না করলে এক্সিকিউশনের ক্রম সিকোয়েন্সিয়াল থাকে। suspend শুধুমাত্র ফাংশনকে থ্রেড ব্লক না করে থামতে এবং করুটিন কনটেক্সটের অংশ হতে দেয়। Continuation Passing Style একটি কম্পাইলেশন মডেল যেখানে প্রতিটি suspend ফাংশন একটি লুকানো Continuation কলব্যাক পায় এবং কম্পাইলার স্থগিতকরণ এবং পুনরায় শুরু পরিচালনার জন্য একটি state machine তৈরি করে।

CoroutineScope এবং কাঠামোবদ্ধ কনকারেন্সি

CoroutineScope একটি কনটেক্সট যা করুটিনের জীবনচক্র নির্ধারণ করে। সমস্ত করুটিন একটি scope-এর ভিতরে লঞ্চ করতে হবে। যখন একটি scope বাতিল হয় (যেমন, Activity শেষ হলে), তার সমস্ত চাইল্ড করুটিন স্বয়ংক্রিয়ভাবে বাতিল হয়। এটি ব্যাকগ্রাউন্ড টাস্ক লিক প্রতিরোধ করে। Android Jetpack প্রতিটি কম্পোনেন্টের জন্য প্রস্তুত scopes প্রদান করে: ViewModel-এর জন্য viewModelScope এবং Activity/Fragment-এর জন্য lifecycleScope, যা সংশ্লিষ্ট কম্পোনেন্ট ধ্বংস হলে স্বয়ংক্রিয়ভাবে বাতিল হয়।

কাঠামোবদ্ধ কনকারেন্সি (Structured Concurrency) একটি নীতি যা গ্যারান্টি দেয় যে একটি করুটিন সম্পূর্ণ হবে না যতক্ষণ না তার সমস্ত চাইল্ড করুটিন সম্পূর্ণ হয়। Job শ্রেণিবিন্যাস একটি গাছ গঠন করে: একটি রুট করুটিন একটি parent job তৈরি করে, চাইল্ডরা child jobs তৈরি করে। parent job বাতিল করা সমস্ত চাইল্ডে ছড়িয়ে পড়ে। Structured Concurrency করুটিন এবং থ্রেডের মধ্যে একটি মৌলিক পার্থক্য।

Scopeকোথায় ব্যবহার হয়বাতিলকরণ
GlobalScopeশুধুমাত্র daemon কাজের জন্যস্বয়ংক্রিয়ভাবে বাতিল হয় না
viewModelScopeAndroid ViewModelViewModel পরিষ্কার হলে
lifecycleScopeAndroid Activity/Fragmentlifecycle ধ্বংস হলে
coroutineScopesuspend ফাংশনের ভিতরেপ্যারেন্ট job বাতিল হলে

ত্রুটি হ্যান্ডলিং-এর জন্য SupervisorJob

সাধারণ Job একটি চাইল্ড করুটিন ব্যর্থ হলে সমস্ত সিবলিং বাতিল করে। SupervisorJob একটি ব্যতিক্রম: একটি চাইল্ড করুটিনে ব্যর্থতা অন্যদের প্রভাবিত করে না। এটি গুরুত্বপূর্ণ যখন একাধিক স্বাধীন কাজ সমান্তরালে এক্সিকিউট হয় এবং তাদের মধ্যে একটি অন্যদের বাতিল না করেই ক্র্যাশ হতে পারে।

Dispatchers এবং করুটিন বিল্ডার

Dispatchers নির্ধারণ করে করুটিন কোন থ্রেডে এক্সিকিউট হয়। Dispatchers.Main — প্রধান Android UI থ্রেড। Dispatchers.IO — ব্লকিং অপারেশন (নেটওয়ার্ক, ডিস্ক) এর জন্য পুল। Dispatchers.Default — CPU-নিবিড় কাজের জন্য। Dispatchers.Unconfined — বর্তমান থ্রেডে শুরু হয় কিন্তু এতে থাকার গ্যারান্টি দেয় না। সঠিক Dispatcher নির্বাচন করা পারফরম্যান্সের জন্য গুরুত্বপূর্ণ: Default-এ IO কাজ কম্পিউট পুল ব্লক করবে, যখন IO-তে CPU কাজ অপ্রয়োজনীয় থ্রেড তৈরি করবে।

withContext — করুটিনের ভিতরে Dispatcher পরিবর্তনের জন্য একটি ফাংশন। উদাহরণস্বরূপ, একটি suspend ফাংশন যা JSON পার্স করে গণনার জন্য Dispatchers.Default-এ সুইচ করতে পারে এবং UI আপডেটের জন্য Dispatchers.Main-এ ফিরে আসতে পারে। withContext Android ডেভেলপমেন্টে সবচেয়ে বেশি ব্যবহৃত বিল্ডার।

তিনটি প্রধান করুটিন বিল্ডার

launch — একটি করুটিন লঞ্চ করে, Job রিটার্ন করে, ফলাফল রিটার্ন করে না (fire-and-forget)। async — একটি করুটিন লঞ্চ করে, Deferred রিটার্ন করে যা থেকে await-এর মাধ্যমে ফলাফল পাওয়া যায়। runBlocking — করুটিন এক্সিকিউট করতে বর্তমান থ্রেড ব্লক করে (শুধুমাত্র পরীক্ষা এবং main ফাংশনের জন্য)। বিল্ডার নির্বাচন পরিস্থিতির উপর নির্ভর করে: launch ইভেন্ট এবং আপডেটের জন্য উপযুক্ত, async ফলাফল সহ কাজের জন্য, runBlocking শুধুমাত্র পরীক্ষা বা এন্ট্রি পয়েন্টের জন্য।

Kotlin-এ করুটিন সহ কোড উদাহরণ

আসুন তিনটি ব্যবহারিক পরিস্থিতি বিবেচনা করি: launch-এর সাথে একটি মৌলিক করুটিন, async-এর সাথে সমান্তরাল কল এবং SupervisorJob-এর সাথে ত্রুটি হ্যান্ডলিং।

launch-এর সাথে করুটিন লঞ্চ করা

viewModelScope.launch ViewModel কনটেক্সটে একটি করুটিন লঞ্চ করে। ViewModel পরিষ্কার হলে করুটিন স্বয়ংক্রিয়ভাবে বাতিল হয়।

kotlin
class ProfileViewModel : ViewModel() {
    fun loadUser() {
        viewModelScope.launch(Dispatchers.IO) {
            val user = api.fetchUser()
            withContext(Dispatchers.Main) {
                showUser(user)
            }
        }
    }
}

async-এর সাথে সমান্তরাল অনুরোধ

coroutineScope async-এর সাথে তিনটি অনুরোধ সমান্তরালে লঞ্চ করে। ফলাফল .await()-এর মাধ্যমে সংগ্রহ করা হয়। কোনো অনুরোধ ব্যর্থ হলে সব বাতিল হয়।

kotlin
suspend fun loadDashboard(): Dashboard = coroutineScope {
    val user = async { api.fetchUser() }
    val posts = async { api.fetchPosts() }
    val stats = async { api.fetchStats() }
    Dashboard(user.await(), posts.await(), stats.await())
}

SupervisorJob-এর সাথে ত্রুটি হ্যান্ডলিং

SupervisorJob প্রতিটি করুটিনকে স্বাধীনভাবে সম্পূর্ণ হতে দেয়। একটি অনুরোধে ত্রুটি অন্যগুলোকে বাতিল করে না।

kotlin
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
    try { api.fetchUsers() } catch (e: Exception) { log(e) }
}
scope.launch {
    try { api.fetchPosts() } catch (e: Exception) { log(e) }
}

করুটিন বনাম থ্রেড: তুলনা এবং পরিস্থিতি

থ্রেড (Thread) একটি অপারেটিং সিস্টেম প্রিমিটিভ। প্রতিটি থ্রেডের নিজস্ব স্ট্যাক (~1 MB) থাকে এবং তৈরি এবং সুইচ করার জন্য সিস্টেম কল প্রয়োজন। করুটিন একটি ভাষা প্রিমিটিভ, OS-এর সাথে আবদ্ধ নয়। তারা অবস্থা সংরক্ষণের জন্য Continuation ব্যবহার করে এবং সিস্টেম কল ছাড়া রানটাইম স্তরে সুইচ করে।

  • মেমরি — থ্রেড ~1 MB, করুটিন ~100 বাইট। 10,000 গুণ পার্থক্য
  • তৈরি — থ্রেড ~1 µs syscall, করুটিন ~0.01 µs JVM স্তরে
  • সুইচিং — থ্রেড ~0.1 µs (syscall), করুটিন ~0.001 µs (continuation)
  • সর্বোচ্চ — হাজার হাজার থ্রেড বনাম লক্ষ লক্ষ করুটিন প্রতি ডিভাইস
  • বাতিলকরণ — থ্রেড বাহ্যিকভাবে বাতিল করা যায় না (অপ্রচলিত Thread.stop), করুটিন Job.cancel()-এর মাধ্যমে বাতিল করা যায়

Google (2025) অনুসারে, থ্রেডের পরিবর্তে করুটিন ব্যবহার করলে Android অ্যাপ্লিকেশনে ব্যাকগ্রাউন্ড কাজের জন্য মেমরি খরচ 90–95% কমে যায়। সমস্ত আধুনিক Android লাইব্রেরি (Retrofit, Room, WorkManager) suspend ফাংশনের মাধ্যমে অন্তর্নির্মিত করুটিন সমর্থন রাখে। Ktor (JetBrains-এর HTTP ক্লায়েন্ট ফ্রেমওয়ার্ক) সম্পূর্ণরূপে করুটিনের উপর নির্মিত, কলব্যাক API ছাড়া প্রতিটি অনুরোধের জন্য suspend ফাংশন প্রদান করে। Room DAO-তে suspend ফাংশনের মাধ্যমে করুটিন সমর্থন করে, যা মূল থ্রেড ব্লক না করে ডাটাবেস কোয়েরি এক্সিকিউট করার অনুমতি দেয়।

কখন করুটিনের পরিবর্তে থ্রেড ব্যবহার করবেন

থ্রেড নেটিভ কোড JNI-এর মাধ্যমে, দীর্ঘমেয়াদী CPU-নিবিড় ব্লকিং কল (ভিডিও রেন্ডারিং, সিমুলেশন) এবং C লাইব্রেরির সাথে সংহতকরণের জন্য প্রয়োজনীয় থাকে। বাকি সবকিছুর জন্য — করুটিন।

প্রায়শই জিজ্ঞাসিত প্রশ্ন

করুটিন থ্রেড থেকে কীভাবে আলাদা?

করুটিন হল কাজের একটি স্থগিতযোগ্য ইউনিট যা একটি বিদ্যমান থ্রেডে এক্সিকিউট হয়। থ্রেড তার নিজস্ব স্ট্যাক সহ একটি সিস্টেম রিসোর্স। করুটিন থ্রেডের চেয়ে হাজার গুণ হালকা এবং স্থগিত হলে রিসোর্স ব্লক করে না।

Dispatchers.IO কী এবং এটি Default থেকে কীভাবে আলাদা?

Dispatchers.IO ব্লকিং I/O অপারেশন (নেটওয়ার্ক, ফাইল) এর জন্য ডিজাইন করা হয়েছে এবং প্রয়োজন হলে নতুন থ্রেড তৈরি করতে পারে। Dispatchers.Default এর CPU-নিবিড় গণনার জন্য একটি নির্দিষ্ট আকারের পুল (CPU কোরের সংখ্যা) রয়েছে।

চলমান করুটিন কীভাবে বাতিল করব?

Job.cancel() করুটিন এবং এর সমস্ত চাইল্ড বাতিল করে। করুটিনের ভিতরে বাতিলকরণ পরীক্ষা করতে, ensureActive() ব্যবহার করুন — করুটিন বাতিল হলে এটি CancellationException নিক্ষেপ করে।

করুটিন কি RxJava-র সাথে ব্যবহার করা যেতে পারে?

হ্যাঁ — kotlinx-coroutines-rx3 লাইব্রেরির মাধ্যমে। এটি Observable/Single-কে suspend ফাংশনে এবং flowable-এর মাধ্যমে ফিরিয়ে আনতে awaitSingle, awaitFirst এবং অন্যান্য ফাংশন প্রদান করে।

করুটিনে Flow কী?

Flow একটি কোল্ড অ্যাসিনক্রোনাস ডেটা স্ট্রিম, করুটিনে RxJava Observable-এর সমতুল্য। Flow ক্রমিকভাবে মান নির্গত করে এবং একটি এক্সেপশন বা সাফল্যের সাথে সম্পূর্ণ হয়। এটি map, filter, catch এবং অন্যান্য অপারেটর সমর্থন করে।

সারাংশ

  • Coroutines — Continuation Passing Style-এর মাধ্যমে ব্লকিং ছাড়া স্থগিতকরণ সহ হালকা Kotlin থ্রেড
  • suspend — স্থগিতযোগ্য ফাংশন চিহ্নিত করার কীওয়ার্ড
  • Dispatchers থ্রেড পুল পরিচালনা করে: Main, IO, Default যথাক্রমে
  • CoroutineScope করুটিন জীবনচক্রকে একটি কম্পোনেন্টের (Activity, ViewModel) সাথে বাঁধে
  • launch ফলাফল ছাড়া করুটিন শুরু করে, async/await — ফলাফল সহ
  • Structured Concurrency চাইল্ড করুটিনের শ্রেণিবদ্ধ বাতিলকরণ নিশ্চিত করে
  • করুটিন বনাম থ্রেড — করুটিন 10,000 গুণ হালকা এবং Android-এর জন্য মান

আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব

IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।

প্রকল্প নিয়ে আলোচনা করুন

আরও পড়ুন