runBlocking — این چیست، پل بلوک‌کننده و چگونه کار می‌کند

نویسنده: IT Sectr منتشر شده: 2026-06-22 زمان مطالعه: 7 دقیقه

runBlocking — Coroutine Builder در Kotlin است که رشته جاری را تا تکمیل کوروتین ارسال شده مسدود می‌کند. برخلاف launch و async، این یک تابع suspend نیست و می‌تواند از کد معمولی (مسدودکننده) فراخوانی شود. به گفته مستندات JetBrains، 2024، runBlocking به عنوان پلی بین دنیای同步 و ناهمگام عمل می‌کند و امکان اجرای کوروتین‌ها از تابع main و تست‌ها را فراهم می‌کند.

نکات اصلی

  • runBlocking — سازنده بلوک‌کننده که CoroutineScope ایجاد کرده و منتظر تکمیل کوروتین می‌ماند
  • مسدودسازی رشته — runBlocking رشته جاری را تا تکمیل کامل کوروتین و همه زیرکوروتین‌ها متوقف می‌کند
  • نقاط ورود — main()، تست‌های JUnit و پل بین کد مسدودکننده و ناهمگام
  • در Main-Thread اندروید ممنوع — فراخوانی runBlocking در رشته UI باعث ANR می‌شود
  • جایگزین‌ها — lifecycleScope، viewModelScope، TestCoroutineDispatcher برای اندروید

runBlocking چیست؟

runBlocking — تابعی در Kotlin است که یک CoroutineScope جدید ایجاد کرده و کوروتین ارسال شده را اجرا می‌کند، رشته جاری را تا تکمیل کامل آن مسدود می‌کند. برخلاف تمام Coroutine Builderهای دیگر، runBlocking یک تابع suspend نیست و می‌تواند از کد同步 معمولی فراخوانی شود. امضای runBlocking یک CoroutineContext و یک بلوک suspend دریافت کرده و نتیجه از نوع T را برمی‌گرداند.

kotlin
public fun <T> runBlocking(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend CoroutineScope.() -> T
): T

runBlocking یک event-loop جدید در رشته جاری راه‌اندازی می‌کند. وقتی کوروتین یک تابع suspend را فراخوانی می‌کند (مثلاً delay() یا await())، runBlocking رشته را مسدود کرده و سایر کوروتین‌های برنامه‌ریزی شده را در همان رشته تا ازسرگیری کوروتین معلق اجرا می‌کند. این مسدودسازی مشارکتی است — رشته بیکار نمی‌ماند، بلکه سایر کوروتین‌ها را پردازش می‌کند.

runBlocking چگونه کار می‌کند

مکانیزم داخلی runBlocking بر پایه event-loop است: هنگام فراخوانی تابع suspend، runBlocking اجرای بلوک جاری را معلق کرده و سایر کوروتین‌ها را از صف اجرا می‌کند. وقتی تابع suspend کامل شد، اجرا ازسر گرفته می‌شود. این چرخه تا زمانی که همه کوروتین‌ها کامل شوند ادامه می‌یابد.

Event-loop در پشت صحنه

runBlocking از استخر تک‌رشته‌ای خود برای اجرای کوروتین‌ها استفاده می‌کند. برخلاف Dispatchers.IO یا Default، runBlocking رشته‌ها را تغییر نمی‌دهد — همه کوروتین‌ها را در رشته جاری پردازش می‌کند و اجرای آنها را میان‌اندازی می‌کند. این تنها builderای است که اجرا در همان رشته را تضمین می‌کند.

kotlin
fun main() {
    val threadName = Thread.currentThread().getName()
    println("قبل از runBlocking روی $threadName")

    val result = runBlocking {
        println("داخل runBlocking روی ${Thread.currentThread().getName()}")
        delay(500L)
        "Done"
    }

    println("بعد از runBlocking: $result")
}

خروجی نشان می‌دهد که هر سه println روی یک رشته اجرا می‌شوند. runBlocking رشته را تغییر نمی‌دهد، بلکه با استفاده از event-loop چندوظیفگی مشارکتی را درون یک رشته سازماندهی می‌کند.

چه زمانی از runBlocking استفاده کنیم

runBlocking در سه سناریو موجه است: نقطه ورود main() در برنامه‌های کنسولی، تست‌های واحد توابع suspend و پل — فراخوانی کد suspend از کتابخانه‌های مبتنی بر callback یا مسدودکننده. در کد تولیدی اندروید استفاده در رشته اصلی قطعاً ممنوع است.

سناریوقابلیت استفادهریسک‌ها
main() برنامه کنسولیبلهندارد — این نقطه ورود است، رشته UI را مسدود نمی‌کند
تست‌های JUnitبلهحداقل — تست‌ها ذاتاً同步 هستند
Android UI-ThreadخیرANR، تأخیر، هنگ کردن رابط کاربری
Callback → Coroutineبله، با احتیاطمسدودسازی استخر رشته‌ها در عملیات طولانی

برای تست‌های اندروید به جای runBlocking از kotlinx-coroutines-test با TestDispatcher استفاده کنید. این امکان کنترل زمان، بازنشانی خودکار و ایزوله‌سازی تست‌ها را فراهم می‌کند.

جایگزین‌های runBlocking

در بیشتر سناریوها runBlocking را می‌توان و باید با جایگزین‌های ناهمگام عوض کرد. برای اندروید اینها عبارتند از viewModelScope، lifecycleScope یا CoroutineScope با dispatcher مناسب. برای تست‌ها — TestCoroutineDispatcher و runTest.

kotlin
    // بد: runBlocking روی رشته اصلی اندروید
runBlocking(Dispatchers.Main) {
    val result = networkApi.fetchData()
    textView.setText(result)
}

// خوب: lifecycleScope
lifecycleScope.launch {
    val result = withContext(Dispatchers.IO) { networkApi.fetchData() }
    textView.setText(result)
}

جایگزین تست‌ها runTest از kotlinx-coroutines-test است. این یک TestCoroutineScope با زمان مجازی ایجاد می‌کند و امکان تست تأخیرها بدون انتظار واقعی را فراهم می‌کند. این کار تست‌ها را سریع‌تر و قطعی می‌کند.

نمونه‌های استفاده از runBlocking

متداول‌ترین سناریو — تست توابع suspend است. runBlocking در تست‌ها امکان انتظار同步 برای نتیجه کوروتین بدون تغییر معماری را فراهم می‌کند. سناریوی دوم — کتابخانه‌هایی با callback API، جایی که توابع suspend از زمینه مسدودکننده از طریق runBlocking فراخوانی می‌شوند.

kotlin
// تست تابع suspend با runBlocking
class RepositoryTest {
    @Test
    fun `fetchUser returns correct data`() {
        val repository = UserRepository(FakeApi())

        val result = runBlocking {
            repository.fetchUser("123")
        }

        assertEquals("John", result.name)
        assertEquals("john@test.com", result.email)
    }
}

برای پل زدن بین دنیای callback و suspend به جای callbackها از CompletableDeferred در ترکیب با runBlocking استفاده کنید — این کار زنجیره عملیات ناهمگام را ساده کرده و خوانایی کد را افزایش می‌دهد.

خطرات استفاده نادرست

استفاده نادرست از runBlocking یکی از اشتباهات رایج هنگام انتقال از رویکرد مسدودکننده به کوروتین‌هاست. مشکلات اصلی: فراخوانی در Main-Thread اندروید، تودرتو کردن runBlocking، استفاده درون توابع ناهمگام و اجرای عملیات طولانی از طریق runBlocking.

  • ANR — runBlocking در Main-Thread اندروید رندر UI را بیش از ۵ ثانیه مسدود می‌کند
  • Deadlock — runBlocking تودرتو درون کوروتین در همان رشته منجر به بن‌بست متقابل می‌شود
  • سردرگمی با dispatcherها — Dispatchers.Main درون runBlocking در رشته پس‌زمینه Looper ندارد و با خطا مواجه می‌شود
  • نشت حافظه — runBlocking هنگام نابودی Activity/Fragment به طور خودکار لغو نمی‌شود

قاعده طلایی: runBlocking یک پل است، نه جایگزین. فقط برای اتصال دنیای مسدودکننده و غیرمسدودکننده از آن استفاده کنید. برای همه وظایف دیگر از launch، async یا lifecycleScope استفاده کنید.

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

چرا runBlocking رشته را مسدود می‌کند اما سایر builderها نه؟

runBlocking تنها builderای است که تابع suspend نیست. این یک event-loop در رشته جاری راه‌اندازی می‌کند و تا تکمیل همه کوروتین‌ها کنترل را برنمی‌گرداند. launch و async بلافاصله کنترل را برمی‌گردانند و کوروتین را در پس‌زمینه اجرا می‌کنند.

آیا می‌توان از runBlocking در Android ViewModel استفاده کرد؟

توصیه نمی‌شود. ViewModel دارای viewModelScope داخلی است که به طور خودکار کوروتین‌ها را مدیریت کرده و هنگام نابودی لغو می‌کند. runBlocking در ViewModel رشته را مسدود کرده و به لغو lifecycle واکنش نشان نمی‌دهد.

runBlocking را در تست‌های واحد با چه چیزی جایگزین کنیم؟

از runTest از کتابخانه kotlinx-coroutines-test استفاده کنید. این یک TestCoroutineScope با کنترل زمان مجازی، لغو خودکار و اجرای قطعی فراهم می‌کند.

Event-loop در runBlocking چیست؟

Event-loop — چرخه پردازش رویدادها درون runBlocking. وقتی کوروتین معلق می‌شود (مثلاً delay())، event-loop به اجرای سایر کوروتین‌های آماده در همان رشته سوئیچ می‌کند. این توهم چندوظیفگی را بدون تغییر رشته ایجاد می‌کند.

اگر runBlocking درون runBlocking فراخوانی شود چه اتفاقی می‌افتد؟

runBlocking تودرتو در یک رشته deadlock ایجاد می‌کند — بلوک بیرونی منتظر داخلی است، اما داخلی نمی‌تواند تا پایان بلوک بیرونی شروع شود. در رشته‌های مختلف این مجاز است، اما به دلیل دشواری اشکال‌زدایی به شدت توصیه نمی‌شود.

خلاصه

  • runBlocking — Coroutine Builder مسدودکننده، پل بین کد مسدودکننده و ناهمگام
  • Event-loop runBlocking کوروتین‌ها را به صورت مشارکتی در یک رشته بدون تغییر پردازش می‌کند
  • سناریوهای مجاز — main()، تست‌های JUnit، پل از کتابخانه‌های callback
  • سناریوهای ممنوع — Android UI-Thread، فراخوانی‌های تودرتو، عملیات طولانی
  • جایگزین‌ها — lifecycleScope، viewModelScope، runTest برای تست‌ها
  • ریسک ANR — runBlocking در Main-Thread بعد از ۵ ثانیه باعث هنگ برنامه می‌شود
  • برای کد تولیدی اندروید از builderهای ناهمگام استفاده کنید — runBlocking برای UI طراحی نشده است

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

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

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

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