runBlocking — cos'è, ponte bloccante e come funziona

Autore: IT Sectr Pubblicato: 2026-06-22 Tempo di lettura: 7 min

runBlocking — un Coroutine Builder in Kotlin che blocca il thread corrente fino al completamento della coroutine passata. A differenza di launch e async, non è una funzione suspend e può essere chiamato da codice normale (bloccante). Secondo la documentazione JetBrains, 2024, runBlocking funge da ponte tra il mondo sincrono e asincrono, consentendo di avviare coroutine dalla funzione main e dai test.

Punti chiave

  • runBlocking — un builder bloccante che crea un CoroutineScope e attende il completamento della coroutine
  • Blocco del thread — runBlocking mantiene il thread corrente fino al completo completamento della coroutine e di tutti i suoi figli
  • Punti di ingresso — main(), test JUnit e ponte tra codice bloccante e asincrono
  • Vietato sul thread principale Android — chiamare runBlocking sul thread UI causa ANR
  • Alternative — lifecycleScope, viewModelScope, TestCoroutineDispatcher per Android

Cos'è runBlocking?

runBlocking è una funzione Kotlin che crea un nuovo CoroutineScope ed esegue la coroutine passata, bloccando il thread corrente fino al suo completo completamento. A differenza di tutti gli altri Coroutine Builder, runBlocking non è una funzione suspend e può essere chiamato da codice sincrono ordinario. La firma di runBlocking accetta un CoroutineContext e un blocco suspend, restituendo un risultato di tipo T.

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

runBlocking avvia un nuovo event-loop sul thread corrente. Quando la coroutine chiama una funzione suspend (ad esempio, delay() o await()), runBlocking blocca il thread ed esegue altre coroutine programmate sullo stesso thread fino a quando quella sospesa non riprende. Questo è blocco cooperativo — il thread non è inattivo ma elabora altre coroutine.

Come funziona runBlocking

Il meccanismo interno di runBlocking si basa su un event-loop: quando viene chiamata una funzione suspend, runBlocking mette in pausa l'esecuzione del blocco corrente ed esegue altre coroutine dalla coda. Quando la funzione suspend termina, l'esecuzione riprende. Questo ciclo continua fino a quando tutte le coroutine non sono terminate.

Event-loop sotto il cofano

runBlocking utilizza il proprio pool a thread singolo per eseguire le coroutine. A differenza di Dispatchers.IO o Default, runBlocking non cambia thread — gestisce tutte le coroutine sul thread corrente, intervallando la loro esecuzione. Questo è l'unico builder che garantisce l'esecuzione sullo stesso thread.

kotlin
fun main() {
    val threadName = Thread.currentThread().getName()
    println("Prima di runBlocking su $threadName")

    val result = runBlocking {
        println("Dentro runBlocking su ${Thread.currentThread().getName()}")
        delay(500L)
        "Done"
    }

    println("Dopo runBlocking: $result")
}

L'output mostrerà che tutte e tre le istruzioni println vengono eseguite sullo stesso thread. runBlocking non cambia thread ma organizza il multitasking cooperativo all'interno di un singolo thread utilizzando un event-loop.

Quando usare runBlocking

runBlocking è giustificato in tre scenari: il punto di ingresso main() nelle applicazioni console, i test unitari delle funzioni suspend e il ponte — chiamare codice suspend da librerie basate su callback o bloccanti. Nel codice Android di produzione, usarlo sul thread principale è severamente vietato.

ScenarioApplicabilitàRischi
main() di applicazione consoleNessuno — è il punto di ingresso, il thread non blocca l'UI
Test JUnitMinimi — i test sono sincroni per definizione
Thread UI AndroidNoANR, rallentamenti, congelamento dell'interfaccia
Callback → CoroutineSì, con cautelaBlocco del pool di thread con operazioni lunghe

Per i test Android, utilizzare kotlinx-coroutines-test con TestDispatcher invece di runBlocking. Questo offre controllo del tempo, pulizia automatica e isolamento dei test.

Alternative a runBlocking

Nella maggior parte degli scenari, runBlocking può e deve essere sostituito con alternative asincrone. Per Android, queste sono viewModelScope, lifecycleScope o CoroutineScope con il dispatcher appropriato. Per i test — TestCoroutineDispatcher e runTest.

kotlin
    // Male: runBlocking sul thread principale Android
runBlocking(Dispatchers.Main) {
    val result = networkApi.fetchData()
    textView.setText(result)
}

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

Per i test, la sostituzione è runTest della libreria kotlinx-coroutines-test. Crea un TestCoroutineScope con tempo virtuale, consentendo di testare i ritardi senza attesa reale. Questo accelera i test e li rende deterministici.

Esempi di utilizzo di runBlocking

Lo scenario più comune è testare le funzioni suspend. runBlocking nei test consente di attendere in modo sincrono un risultato della coroutine senza modificare l'architettura. Il secondo scenario riguarda le librerie con API di callback, dove le funzioni suspend vengono chiamate da un contesto bloccante tramite runBlocking.

kotlin
// Testare funzione suspend con 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)
    }
}

Per il ponte tra i mondi callback e suspend, utilizzare CompletableDeferred in combinazione con runBlocking invece di callback — questo semplifica le catene di operazioni asincrone e migliora la leggibilità del codice.

Pericoli di un uso improprio

L'uso improprio di runBlocking è uno degli errori comuni durante la transizione da un approccio bloccante alle coroutine. Principali problemi: chiamata sul thread principale Android, annidamento di runBlocking, uso all'interno di funzioni asincrone ed esecuzione di operazioni lunghe tramite runBlocking.

  • ANR — runBlocking sul thread principale Android blocca il rendering dell'UI per più di 5 secondi
  • Deadlock — runBlocking annidato all'interno di una coroutine sullo stesso thread porta a un blocco reciproco
  • Confusione di dispatcher — Dispatchers.Main all'interno di runBlocking su un thread in background non ha Looper e genera un'eccezione
  • Perdite di memoria — runBlocking non viene automaticamente annullato alla distruzione di Activity/Fragment

Regola d'oro: runBlocking è un ponte, non una sostituzione. Usalo solo per connettere mondi bloccanti e non bloccanti. Per tutte le altre attività, usa launch, async o lifecycleScope.

Domande frequenti

Perché runBlocking blocca il thread mentre altri builder no?

runBlocking è l'unico builder che non è una funzione suspend. Avvia un event-loop sul thread corrente e non restituisce il controllo fino a quando tutte le coroutine non sono completate. launch e async restituiscono il controllo immediatamente, eseguendo la coroutine in background.

Si può usare runBlocking nel ViewModel Android?

Sconsigliato. ViewModel dispone di un viewModelScope integrato che gestisce automaticamente le coroutine e le annulla alla distruzione. runBlocking in ViewModel blocca il thread e non risponde all'annullamento del ciclo di vita.

Cosa usare al posto di runBlocking nei test unitari?

Usare runTest dalla libreria kotlinx-coroutines-test. Fornisce un TestCoroutineScope con controllo del tempo virtuale, annullamento automatico ed esecuzione deterministica.

Cos'è un event-loop in runBlocking?

Event-loop è un ciclo di elaborazione degli eventi all'interno di runBlocking. Quando una coroutine viene sospesa (es. delay()), l'event-loop passa all'esecuzione di altre coroutine pronte sullo stesso thread. Questo crea l'illusione del multitasking senza cambio di thread.

Cosa succede se si chiama runBlocking dentro runBlocking?

runBlocking annidato sullo stesso thread crea un deadlock — il blocco esterno attende quello interno, ma quello interno non può iniziare fino a quando l'esterno non termina. Su thread diversi è consentito ma fortemente sconsigliato a causa della complessità di debug.

Riepilogo

  • runBlocking — un Coroutine Builder bloccante, ponte tra codice bloccante e asincrono
  • Event-loop runBlocking elabora le coroutine in modo cooperativo su un singolo thread senza cambio
  • Scenari consentiti — main(), test JUnit, ponte da librerie di callback
  • Scenari vietati — thread UI Android, chiamate annidate, operazioni lunghe
  • Alternative — lifecycleScope, viewModelScope, runTest per i test
  • Rischio ANR — runBlocking sul thread principale blocca l'app dopo 5 secondi
  • Per il codice Android di produzione, usa builder asincroni — runBlocking non è progettato per l'UI

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche