runBlocking — co to jest, blokujący most i jak działa

Autor: IT Sectr Opublikowano: 2026-06-22 Czas czytania: 7 min

runBlocking — Coroutine Builder w Kotlinie, który blokuje bieżący wątek do czasu zakończenia przekazanej korutyny. W przeciwieństwie do launch i async nie jest funkcją suspend i może być wywoływany ze zwykłego (blokującego) kodu. Według dokumentacji JetBrains, 2024, runBlocking służy jako most między synchronicznym a asynchronicznym światem, umożliwiając uruchamianie korutyn z funkcji main i testów.

Najważniejsze

  • runBlocking — blokujący builder, tworzący CoroutineScope i oczekujący na zakończenie korutyny
  • Blokowanie wątku — runBlocking wstrzymuje bieżący wątek do całkowitego zakończenia korutyny i wszystkich podrzędnych
  • Punkty wejścia — main(), testy JUnit i pomost między kodem blokującym a asynchronicznym
  • Zakazany na Main-Thread Androida — wywołanie runBlocking w wątku UI powoduje ANR
  • Alternatywy — lifecycleScope, viewModelScope, TestCoroutineDispatcher dla Androida

Co to jest runBlocking?

runBlocking — to funkcja Kotlina, która tworzy nowy CoroutineScope i uruchamia przekazaną korutynę, blokując bieżący wątek do jej całkowitego zakończenia. W przeciwieństwie do wszystkich innych Coroutine Builder, runBlocking nie jest funkcją suspend i może być wywoływany ze zwykłego synchronicznego kodu. Sygnatura runBlocking przyjmuje CoroutineContext i blok suspend, zwracając wynik typu T.

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

runBlocking uruchamia nową pętlę zdarzeń (event-loop) w bieżącym wątku. Gdy korutyna wywołuje funkcję suspend (np. delay() lub await()), runBlocking blokuje wątek i wykonuje inne zaplanowane korutyny w tym samym wątku do czasu wznowienia zawieszonej. Jest to kooperatywne blokowanie — wątek nie marnuje czasu, ale przetwarza inne korutyny.

Jak działa runBlocking

Wewnętrzny mechanizm runBlocking opiera się na pętli zdarzeń: przy wywołaniu funkcji suspend runBlocking wstrzymuje wykonanie bieżącego bloku i uruchamia inne korutyny z kolejki. Gdy funkcja suspend się zakończy, wykonanie zostaje wznowione. Cykl ten trwa, aż wszystkie korutyny się zakończą.

Event-loop pod maską

runBlocking używa własnej jednowątkowej puli do wykonywania korutyn. W przeciwieństwie do Dispatchers.IO lub Default, runBlocking nie przełącza wątków — przetwarza wszystkie korutyny w bieżącym wątku, przeplatając ich wykonanie. Jest to jedyny builder gwarantujący wykonanie w tym samym wątku.

kotlin
fun main() {
    val threadName = Thread.currentThread().getName()
    println("Przed runBlocking na $threadName")

    val result = runBlocking {
        println("Wewnątrz runBlocking na ${Thread.currentThread().getName()}")
        delay(500L)
        "Done"
    }

    println("Po runBlocking: $result")
}

Wynik pokaże, że wszystkie trzy println wykonują się w jednym wątku. runBlocking nie przełącza wątku, ale organizuje kooperatywną wielozadaniowość wewnątrz jednego wątku za pomocą pętli zdarzeń.

Kiedy używać runBlocking

runBlocking jest uzasadniony w trzech scenariuszach: punkt wejścia main() w aplikacjach konsolowych, testy jednostkowe funkcji suspend oraz pomost — wywoływanie kodu suspend z bibliotek opartych na callback lub blokujących. W kodzie produkcyjnym Androida użycie na głównym wątku jest kategorycznie zabronione.

ScenariuszStosowalnośćRyzyka
main() aplikacji konsolowejTakBrak — to punkt wejścia, wątek nie blokuje UI
Testy JUnitTakMinimalne — testy są z definicji synchroniczne
Android UI-ThreadNieANR, opóźnienia, zawieszenie interfejsu
Callback → CoroutineTak, ostrożnieBlokowanie puli wątków przy długotrwałych operacjach

Do testów na Androidzie używaj kotlinx-coroutines-test z TestDispatcher zamiast runBlocking. Daje to kontrolę nad czasem, automatyczne resetowanie i izolację testów.

Alternatywy dla runBlocking

Większość scenariuszy runBlocking można i należy zastąpić alternatywami asynchronicznymi. Dla Androida są to viewModelScope, lifecycleScope lub CoroutineScope z odpowiednim dyspozytorem. Dla testów — TestCoroutineDispatcher i runTest.

kotlin
    // Źle: runBlocking na głównym wątku Androida
runBlocking(Dispatchers.Main) {
    val result = networkApi.fetchData()
    textView.setText(result)
}

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

Zamiennikiem dla testów jest runTest z kotlinx-coroutines-test. Tworzy on TestCoroutineScope z wirtualnym czasem, pozwalając testować opóźnienia bez rzeczywistego czekania. To przyspiesza testy i czyni je deterministycznymi.

Przykłady użycia runBlocking

Najczęstszy scenariusz to testowanie funkcji suspend. runBlocking w testach pozwala synchronicznie poczekać na wynik korutyny bez zmiany architektury. Drugi scenariusz to biblioteki z callback API, gdzie funkcje suspend są wywoływane z kontekstu blokującego przez runBlocking.

kotlin
// Test funkcji suspend za pomocą 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)
    }
}

Do pomostowania między światem callback a suspend używaj CompletableDeferred w kombinacji z runBlocking zamiast callbacków — upraszcza to łańcuchy operacji asynchronicznych i zwiększa czytelność kodu.

Zagrożenia nieprawidłowego użycia

Nieprawidłowe użycie runBlocking to jeden z częstych błędów przy przejściu z podejścia blokującego na korutyny. Główne problemy: wywołanie na Main-Thread Androida, zagnieżdżanie runBlocking, użycie wewnątrz funkcji asynchronicznych i uruchamianie długotrwałych operacji przez runBlocking.

  • ANR — runBlocking na Main-Thread Androida blokuje renderowanie UI na ponad 5 sekund
  • Deadlock — zagnieżdżony runBlocking wewnątrz korutyny w tym samym wątku prowadzi do wzajemnej blokady
  • Problemy z dyspozytorami — Dispatchers.Main wewnątrz runBlocking w tle nie ma Looper i kończy się wyjątkiem
  • Wycieki pamięci — runBlocking nie jest automatycznie anulowany przy zniszczeniu Activity/Fragment

Złota zasada: runBlocking to most, a nie zamiennik. Używaj go tylko do łączenia świata blokującego i nieblokującego. Do wszystkich innych zadań stosuj launch, async lub lifecycleScope.

Często zadawane pytania

Dlaczego runBlocking blokuje wątek, a inne buildery — nie?

runBlocking to jedyny builder, który nie jest funkcją suspend. Uruchamia pętlę zdarzeń w bieżącym wątku i nie zwraca kontroli, dopóki wszystkie korutyny nie zostaną zakończone. launch i async zwracają kontrolę natychmiast, wykonując korutynę w tle.

Czy można używać runBlocking w Android ViewModel?

Nie zaleca się. ViewModel ma wbudowany viewModelScope, który automatycznie zarządza korutynami i anuluje je przy zniszczeniu. runBlocking w ViewModel blokuje wątek i nie reaguje na anulowanie lifecycle.

Czym zastąpić runBlocking w testach jednostkowych?

Używaj runTest z biblioteki kotlinx-coroutines-test. Zapewnia on TestCoroutineScope z kontrolą wirtualnego czasu, automatycznym anulowaniem i deterministycznym wykonaniem.

Czym jest event-loop w runBlocking?

Event-loop — pętla przetwarzania zdarzeń wewnątrz runBlocking. Gdy korutyna jest zawieszona (np. delay()), event-loop przełącza się na wykonanie innych gotowych korutyn w tym samym wątku. Tworzy to iluzję wielozadaniowości bez przełączania wątków.

Co się stanie przy wywołaniu runBlocking wewnątrz runBlocking?

Zagnieżdżony runBlocking w jednym wątku tworzy deadlock — zewnętrzny blok czeka na wewnętrzny, ale wewnętrzny nie może się rozpocząć, dopóki zewnętrzny się nie zakończy. W różnych wątkach jest to dopuszczalne, ale skrajnie niezalecane ze względu na trudność debugowania.

Podsumowanie

  • runBlocking — blokujący Coroutine Builder, most między kodem blokującym a asynchronicznym
  • Event-loop runBlocking przetwarza korutyny kooperatywnie w jednym wątku bez przełączania
  • Dozwolone scenariusze — main(), testy JUnit, pomost z bibliotek callback
  • Zakazane scenariusze — Android UI-Thread, zagnieżdżone wywołania, długotrwałe operacje
  • Alternatywy — lifecycleScope, viewModelScope, runTest dla testów
  • Ryzyko ANR — runBlocking na Main-Thread powoduje zawieszenie aplikacji po 5 sekundach
  • Do kodu produkcyjnego Androida używaj asynchronicznych builderów — runBlocking nie jest przeznaczony do UI

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również