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 مشکل callback hell را مشابه async/await حل می‌کنند، اما API غنی‌تری ارائه می‌دهند: کانال‌ها (Channel)، Flow، مدیریت استثنا در سلسله‌مراتب Job و یکپارچگی مستقیم با Android Lifecycle.

به گزارش JetBrains (2025)، هر کوروتین حدود 100 بایت حافظه مصرف می‌کند در مقابل 1+ مگابایت برای یک نخ بومی. این امکان اجرای میلیون‌ها کوروتین در یک برنامه را بدون خطر OutOfMemoryError فراهم می‌کند. دقیقاً سبکی کوروتین‌ها آنها را به ابزار ترجیحی برای ناهمزمانی در Android تبدیل می‌کند.

کوروتین‌ها در زیر کاپوت چگونه کار می‌کنند

هر کوروتین Kotlin از طریق Continuation Passing Style (CPS) به یک ماشین حالت کامپایل می‌شود. کامپایلر یک پارامتر مخفی Continuation به هر تابع suspend اضافه می‌کند. 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 یک callback مخفی Continuation دریافت می‌کند و کامپایلر یک ماشین حالت برای مدیریت توقف‌ها و ازسرگیری‌ها تولید می‌کند.

CoroutineScope و Structured Concurrency

CoroutineScope — زمینه‌ای است که چرخه حیات کوروتین‌ها را تعیین می‌کند. همه کوروتین‌ها باید درون یک scope راه‌اندازی شوند. وقتی scope لغو می‌شود (مثلاً هنگام پایان Activity)، تمام کوروتین‌های فرزند آن به طور خودکار لغو می‌شوند. این کار از نشت وظایف پس‌زمینه جلوگیری می‌کند. Android Jetpack scopeهای آماده‌ای برای هر کامپوننت فراهم می‌کند: viewModelScope برای ViewModel و lifecycleScope برای Activity و Fragment که با نابودی کامپوننت مربوطه به طور خودکار لغو می‌شوند.

Structured Concurrency — اصلی که تضمین می‌کند کوروتین تا زمانی که تمام کوروتین‌های فرزندش کامل نشده‌اند، پایان نمی‌یابد. سلسله‌مراتب Job یک درخت تشکیل می‌دهد: کوروتین ریشه یک parent job ایجاد می‌کند، فرزندان child jobs ایجاد می‌کنند. لغو parent job به همه فرزندان سرایت می‌کند. Structured Concurrency تفاوت اساسی بین کوروتین‌ها و نخ‌ها است.

Scopeمحل استفادهلغو
GlobalScopeفقط برای وظایف daemonبه طور خودکار لغو نمی‌شود
viewModelScopeAndroid ViewModelهنگام پاکسازی ViewModel
lifecycleScopeAndroid Activity/Fragmentهنگام نابودی lifecycle
coroutineScopeدرون تابع suspendهنگام لغو job والد

SupervisorJob برای مدیریت خطا

Job معمولی هنگام خطای یک کوروتین فرزند، تمام siblings را لغو می‌کند. SupervisorJob یک استثناست: خرابی در یک کوروتین فرزند روی بقیه تأثیر نمی‌گذارد. این زمانی مهم است که چندین وظیفه مستقل به صورت موازی اجرا می‌شوند و یکی ممکن است بدون نیاز به لغو بقیه شکست بخورد.

Dispatchers و سازنده‌های کوروتین

Dispatchers تعیین می‌کنند کوروتین‌ها روی کدام نخ‌ها اجرا شوند. Dispatchers.Main — نخ اصلی UI اندروید. Dispatchers.IO — استخر برای عملیات‌های مسدودکننده (شبکه، دیسک). Dispatchers.Default — برای وظایف CPU-intensive. Dispatchers.Unconfined — در نخ جاری شروع می‌شود اما تضمینی برای ماندن روی آن ندارد. انتخاب Dispatcher مناسب برای کارایی حیاتی است: وظیفه IO روی Default استخر را برای محاسبات مسدود می‌کند و وظیفه CPU روی IO نخ‌های اضافی ایجاد می‌کند.

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 مگابایت) دارد و برای ایجاد و جابجایی نیاز به فراخوانی سیستم دارد. کوروتین‌ها — ابتدایی زبان هستند که به سیستم عامل وابسته نیستند. آنها از Continuation برای ذخیره وضعیت استفاده می‌کنند و در سطح runtime بدون فراخوانی سیستم جابجا می‌شوند.

  • حافظه — نخ ~1 مگابایت، کوروتین ~100 بایت. تفاوت 10 000 برابر
  • ایجاد — نخ ~1 میکروثانیه syscall، کوروتین ~0.01 میکروثانیه در سطح JVM
  • جابجایی — نخ ~0.1 میکروثانیه (syscall)، کوروتین ~0.001 میکروثانیه (continuation)
  • حداکثر — هزاران نخ در مقابل میلیون‌ها کوروتین روی دستگاه
  • لغو — نخ را نمی‌توان از بیرون لغو کرد (Thread.stop منسوخ شده)، کوروتین از طریق Job.cancel() قابل لغو است

به گزارش Google (2025)، استفاده از کوروتین‌ها به جای نخ‌ها مصرف حافظه را برای وظایف پس‌زمینه در برنامه‌های Android تا 90–95٪ کاهش می‌دهد. تمام کتابخانه‌های مدرن Android (Retrofit, Room, WorkManager) پشتیبانی داخلی از کوروتین‌ها را از طریق توابع suspend دارند. Ktor (چارچوب HTTP کلاینت JetBrains) نیز کاملاً روی کوروتین‌ها ساخته شده است و برای هر درخواست بدون API callback توابع suspend ارائه می‌دهد. Room از کوروتین‌ها از طریق توابع suspend در DAO پشتیبانی می‌کند که امکان اجرای پرس‌وجوهای پایگاه داده بدون مسدود کردن نخ اصلی را فراهم می‌کند.

چه زمانی به جای کوروتین‌ها از نخ‌ها استفاده کنیم

نخ‌ها برای کد بومی از طریق JNI، فراخوانی‌های مسدودکننده CPU-intensive بدون محدودیت زمانی (رندر ویدئو، شبیه‌سازی‌ها) و یکپارچگی با کتابخانه‌های C ضروری باقی می‌مانند. برای بقیه موارد — کوروتین‌ها.

سوالات متداول

کوروتین چه تفاوتی با نخ دارد؟

کوروتین — یک واحد کار قابل توقف است که روی یک نخ موجود اجرا می‌شود. نخ یک منبع سیستمی با پشته مخصوص خود است. کوروتین‌ها هزاران بار سبک‌تر از نخ‌ها هستند و هنگام توقف منابع را مسدود نمی‌کنند.

Dispatchers.IO چیست و چه تفاوتی با Default دارد؟

Dispatchers.IO برای عملیات‌های I/O مسدودکننده (شبکه، فایل‌ها) طراحی شده و می‌تواند در صورت نیاز نخ‌های جدید ایجاد کند. Dispatchers.Default استخری با اندازه ثابت (تعداد هسته‌های پردازنده) برای محاسبات CPU-intensive دارد.

چگونه یک کوروتین در حال اجرا را لغو کنیم؟

Job.cancel() کوروتین و تمام فرزندانش را لغو می‌کند. برای بررسی لغو درون کوروتین از ensureActive() استفاده کنید — اگر کوروتین لغو شده باشد CancellationException پرتاب می‌کند.

آیا می‌توان از کوروتین‌ها با RxJava استفاده کرد؟

بله — از طریق کتابخانه kotlinx-coroutines-rx3. توابع awaitSingle، awaitFirst و دیگر توابع را برای تبدیل Observable/Single به توابع suspend و بالعکس از طریق flowable ارائه می‌دهد.

Flow در کوروتین‌ها چیست؟

Flow — یک جریان داده ناهمزمان سرد، مشابه Observable در RxJava برای کوروتین‌ها است. Flow مقادیر را به صورت ترتیبی منتشر می‌کند و با استثنا یا موفقیت پایان می‌یابد. از عملگرهای map، filter، catch و دیگر عملگرها پشتیبانی می‌کند.

خلاصه

  • Coroutines — نخ‌های سبک Kotlin با توقف بدون مسدودسازی از طریق Continuation Passing Style
  • suspend — کلمه کلیدی برای علامت‌گذاری توابع قابل توقف
  • Dispatchers استخر نخ‌ها را مدیریت می‌کنند: Main، IO، Default به ترتیب
  • CoroutineScope چرخه حیات کوروتین‌ها را به کامپوننت متصل می‌کند (Activity, ViewModel)
  • launch کوروتین را بدون نتیجه راه‌اندازی می‌کند، async/await — با نتیجه
  • Structured Concurrency لغو سلسله‌مراتبی کوروتین‌های فرزند را تضمین می‌کند
  • کوروتین‌ها در مقابل نخ‌ها — کوروتین‌ها 10 000 بار سبک‌تر هستند و استاندارد Android هستند

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید