Dispatchers — Schlüsselkonzepte, Arten von Dispatchern und wie sie funktionieren

Autor: IT Sectr Veröffentlicht: 2026-06-22 Lesezeit: 9 Min.

Dispatchers in Kotlin Coroutines sind CoroutineContext-Komponenten, die die Threads für die Ausführung von Coroutinen bestimmen: Main (UI-Thread), IO (Netzwerk und Festplatte), Default (CPU-intensive Aufgaben) und Unconfined (aktueller Thread). Jeder Dispatcher verwaltet einen spezialisierten Thread-Pool, der für eine bestimmte Art von Arbeit optimiert ist. Laut JetBrains-Leitfaden, 2024 ist die Wahl des richtigen Dispatchers entscheidend für die Leistung und Stabilität der Anwendung.

Wichtige Erkenntnisse

  • Dispatchers — vordefinierte Coroutine-Dispatcher, die die Aufgabenverteilung über Threads verwalten
  • Dispatchers.Main — Android-UI-Thread, zum Aktualisieren der Oberfläche und Arbeiten mit View
  • Dispatchers.IO — Pool für Netzwerkanfragen, Datei-Lesen/Schreiben und DB (bis zu 64 Threads)
  • Dispatchers.Default — Pool für CPU-intensive Aufgaben basierend auf der Anzahl der Prozessorkerne
  • Dispatchers.Unconfined — erbt den aktuellen Thread, ohne Umschaltung

Was sind Dispatchers?

Dispatchers sind Implementierungen des CoroutineDispatcher-Interfaces, die Elemente von CoroutineContext sind. Sie bestimmen, in welchem Thread oder Thread-Pool die Coroutine ausgeführt wird. Beim Erstellen einer Coroutine über launch oder async kann der Dispatcher als erster Parameter übergeben werden: launch(Dispatchers.IO) { ... }. Wenn kein Dispatcher angegeben ist, wird er vom äußeren CoroutineScope geerbt.

Kotlin bietet vier integrierte Dispatcher: Main, IO, Default, Unconfined. Jeder Dispatcher verwendet seinen eigenen Thread-Pool, der für eine bestimmte Art von Operation optimiert ist. Die Wahl des richtigen Dispatchers bestimmt die Anwendungsleistung: Eine falsche Wahl führt zu UI-Verzögerungen, untätigen CPU-Kernen oder ineffizienter Thread-Nutzung.

DispatcherThread-PoolMax. ThreadsVerwendung
Dispatchers.MainEiner (UI)1UI-Updates, LiveData, View
Dispatchers.IOIO-Pool64 (limitedParallelism)Netzwerk, Dateien, DB
Dispatchers.DefaultCPU-PoolN KerneSortieren, Parsen, Berechnungen
Dispatchers.UnconfinedAktueller ThreadN/VZwischenoperationen, Tests

Dispatchers.Main: UI-Thread

Dispatchers.Main ist der Dispatcher, der Coroutinen auf dem Android-Hauptthread ausführt. Er ist für UI-bezogene Operationen konzipiert: Aktualisieren von TextView, Aufrufen von notifyDataSetChanged, Arbeiten mit LiveData und StateFlow. In Android wird dieser Dispatcher über Handler (Looper.getMainLooper()) implementiert.

kotlin
// Correct switch to Main for UI updates
viewModelScope.launch(Dispatchers.IO) {
    val data = repository.fetchData()
    withContext(Dispatchers.Main) {
        _uiState.value = data
    }
}

Wenn sich eine Coroutine bereits im Main-Dispatcher befindet, erzeugt ein zusätzliches withContext(Dispatchers.Main) keinen Overhead — der Dispatcher überprüft den aktuellen Thread und überspringt die Umschaltung. withContext ist die bevorzugte Methode zum Wechseln zwischen Dispatchern.

Dispatchers.IO: Netzwerk- und Festplattenoperationen

Dispatchers.IO ist ein für E/A-Operationen optimierter Dispatcher: HTTP-Anfragen (Ktor, OkHttp), Lesen und Schreiben von Dateien, Arbeiten mit Room oder SQLDelight. Er verwendet standardmäßig einen Pool von 64 Threads, der unter Last skalierbar ist. Jede neue E/A-Anfrage kann einen zusätzlichen Thread erstellen, bis das Limit erreicht ist.

Parallelitätseinschränkung

Um die Anzahl gleichzeitiger E/A-Operationen zu steuern, verwenden Sie limitedParallelism(). Diese Funktion erstellt einen neuen Dispatcher mit einer Begrenzung der Anzahl paralleler Threads und verhindert so die Erschöpfung des Pools bei Massenoperationen.

kotlin
val limitedIo = Dispatchers.IO.limitedParallelism(4)

// Load 100 files with limit of 4 concurrent operations
coroutineScope {
    val files = (1..100).map { index ->
        async(limitedIo) {
            downloadFile("file_$index")
        }
    }
    files.awaitAll()
}

Verwenden Sie den IO-Dispatcher für alle Operationen, bei denen die Coroutine Zeit mit Warten verbringt (I/O-gebunden). CPU-intensive Aufgaben auf dem IO-Dispatcher sind ineffizient — sie belegen Threads, die für E/A bestimmt sind, und verringern den Durchsatz des Systems.

Dispatchers.Default: CPU-intensive Aufgaben

Dispatchers.Default ist der Dispatcher für Rechenoperationen, die den Prozessor belasten: Sortieren, Filtern, JSON-Parsing (Moshi, Kotlinx Serialization), Bildverarbeitung, Berechnungen. Die Poolgröße entspricht der Anzahl der Prozessorkerne (jedoch nicht weniger als 2). Dies gewährleistet eine maximale CPU-Auslastung ohne Kontextwechsel.

kotlin
suspend fun processData(input: List<RawRecord>): List<ProcessedRecord> {
    return withContext(Dispatchers.Default) {
        input
            .parallelStream()
            .map { transform(it) }
            .toList()
    }
}

Verwenden Sie Dispatchers.Default nicht für E/A-Operationen — dies blockiert Threads des CPU-Pools, die Rechenaufgaben verarbeiten könnten. Die Trennung von IO und Default ermöglicht eine optimale Nutzung der Systemressourcen: IO-Threads warten auf E/A, CPU-Threads sind ständig mit Berechnungen beschäftigt.

Dispatchers.Unconfined: Aktueller Thread

Dispatchers.Unconfined ist ein spezieller Dispatcher, der eine Coroutine an keinen Pool bindet. Die Coroutine beginnt die Ausführung in dem Thread, in dem launch/async aufgerufen wurde, und wird nach der Aussetzung in dem Thread fortgesetzt, der resume aufgerufen hat. Dieses Verhalten eignet sich für Zwischenoperationen, die keinen festen Kontext erfordern.

kotlin
fun main() = runBlocking {
    launch(Dispatchers.Unconfined) {
        println("Before delay: ${Thread.currentThread().getName()}")
        delay(500L)
        println("After delay: ${Thread.currentThread().getName()}")
    }
}

In Produktionscode wird Dispatchers.Unconfined selten verwendet. Hauptanwendungsfälle: leichte Transformationen vor der Übergabe von Daten an einen anderen Dispatcher und Tests. Für Produktionslasten verwenden Sie explizite Dispatcher — Unconfined ist unvorhersehbar, da der Ausführungsthread von der Resume-Implementierung abhängt.

Wie wählt man einen Dispatcher aus

Die Dispatcher-Auswahl hängt vom Aufgabentyp ab: UI-Operationen → Main, I/O-gebunden → IO, CPU-gebunden → Default, Zwischenoperationen → vom Gültigkeitsbereich erben. Für Android wird empfohlen, eine Coroutine auf dem Dispatcher zu starten, auf dem die Hauptarbeit ausgeführt wird, und vor dem Aktualisieren der UI über withContext auf Main umzuschalten.

  • Führen Sie keine E/A-Operationen auf dem Main-Dispatcher aus — dies blockiert die UI
  • Führen Sie keine CPU-intensiven Aufgaben auf dem IO-Dispatcher aus — dies verbraucht ineffizient Threads des IO-Pools
  • Verwenden Sie limitedParallelism zur Steuerung der Parallelität bei Massen-E/A-Operationen
  • Wechseln Sie den Dispatcher mit withContext, nicht durch Erstellen eines neuen Gültigkeitsbereichs
  • Erben Sie den Dispatcher vom Gültigkeitsbereich, wenn die Coroutine keinen spezifischen Pool benötigt

Für komplexe Szenarien kombinieren Sie Dispatcher mit dem +-Operator: Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler. Dies erstellt einen CoroutineContext mit einem bestimmten Dispatcher, Fehlerbehandlung und einer isolierten Job-Hierarchie.

Häufig gestellte Fragen

Was ist der Unterschied zwischen Dispatchers.IO und Dispatchers.Default?

Dispatchers.IO verwendet einen Pool von bis zu 64 Threads für I/O-gebundene Operationen (Warten auf E/A), während Dispatchers.Default einen Pool basierend auf der Anzahl der CPU-Kerne für Rechenaufgaben verwendet. Bei Thread-Knappheit können beide Pools Threads miteinander teilen.

Kann ich meinen eigenen Dispatcher in Kotlin Coroutines erstellen?

Ja, verwenden Sie newSingleThreadContext() für einen Einzelthread oder newFixedThreadPoolContext() für einen festen Pool. Für die Produktion verwenden Sie limitedParallelism() basierend auf vorhandenen Dispatchern — dies ist effizienter als das Erstellen neuer Pools.

Was passiert, wenn Dispatchers.Main von einem Hintergrundthread aufgerufen wird?

Wenn Dispatchers.Main nicht verfügbar ist (z.B. in einem JUnit-Test oder Hintergrunddienst), wird eine IllegalStateException ausgelöst. Verwenden Sie TestCoroutineDispatcher für Tests und Dispatchers.IO oder Default für Hintergrunddienste.

Wie begrenze ich die Anzahl paralleler E/A-Operationen?

Verwenden Sie Dispatchers.IO.limitedParallelism(N), wobei N die maximale Anzahl paralleler Threads ist. Dies verhindert die Erschöpfung des Pools bei Massenanfragen und bietet kontrollierte Parallelität.

Wann verwendet man Dispatchers.Unconfined?

Dispatchers.Unconfined eignet sich für Zwischenoperationen: leichte Datentransformationen vor der Übergabe an einen anderen Dispatcher, Testszenarien. In produktivem Android-Code wird es aufgrund des undefinierten Ausführungsthreads nach der Aussetzung nicht empfohlen.

Zusammenfassung

  • Dispatchers — CoroutineContext-Komponenten, die Coroutine-Ausführungsthreads bestimmen
  • Dispatchers.Main — für Android-UI-Operationen, ein Hauptthread
  • Dispatchers.IO — für Netzwerk- und Festplattenoperationen, Pool von bis zu 64 Threads
  • Dispatchers.Default — für CPU-intensive Berechnungen, Pool basierend auf Kernanzahl
  • Dispatchers.Unconfined — ohne Thread-Bindung, für Zwischenoperationen
  • withContext — der Hauptmechanismus zum Wechseln zwischen Dispatchern innerhalb einer Coroutine
  • Die Wahl des falschen Dispatchers führt zu UI-Verzögerungen oder ineffizienter Ressourcennutzung

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch