Диспетчер корутины

Последнее обновление: 26.06.2021

Контекст корутины включает себя такой элемент как диспетчер корутины. Диспетчер корутины определяет какой поток или какие потоки будут использоваться для выполнения корутины.

Все построители корутины, в частности, функции launch и async в качестве необязательного параметра принимают объект типа CoroutineContext, который может использоваться для определения диспетчера создаваемой корутины.

Когда функция launch вызывается без параметров, она перенимает контекст, в котором она создается и запускается. Например, используем метод Thread.currentThread(), который предоставляет JDK, чтобы получить данные потока корутины:

import kotlinx.coroutines.*

suspend fun main() = coroutineScope{

    launch {
        println("Корутина выполняется на потоке: ${Thread.currentThread().name}")
    }
    println("Функция main выполняется на потоке: ${Thread.currentThread().name}")
}

Здесь с помощью переменной Thread.currentThread().name мы можем получить имя потока. И в данном случае мы получим консольный вывод наподобие следующего:

Функция main выполняется на потоке: main
Корутина выполняется на потоке: DefaultDispatcher-worker-1

Мы видим, что функция main выполняется на потоке под названием "main" (который собственно и отведен для выполнения этой функции), а корутина выполняется на другом потоке с названием DefaultDispatcher-worker-1. Если мы обратимся к отладчику корутин, то мы сможем увидеть применяемый корутиной диспетчер:

Coroutine Dispatcher in Kotlin

Здесь мы видим, что корутина, которая выполняется в потоке "DefaultDispatcher-worker-1", применяет диспетчер Dispatcher.Default.

Поскольку контекст корутин в функции main создается в данном случае с помощью функции coroutineScope, которая устанавливает для создаваемых корутин по умолчанию диспетчер типа Dispatcher.Default. И, корутина, определенная в примере выше перенимает этот контекст вместе с данным типом диспетчера.

Что это значит? Рассмотрим доступные типы диспетчеров:

  • Dispatchers.Default: применяется по умолчанию, если тип диспетчера не указан явным образом. Этот тип использует общий пул разделяемых фоновых потоков и подходит для вычислений, которые не работают с операциями ввода-вывода (операциями с файлами, базами данных, сетью) и которые требуют интенсивного потребления ресурсов центрального процессора.

  • Dispatchers.IO: использует общий пул потоков, создаваемых по мере необходимости, и предназначен для выполнения операций ввода-вывода (например, операции с файлами или сетевыми запросами).

  • Dispatchers.Main: применяется в графических приложениях, например, в приложениях Android или JavaFX.

  • Dispatchers.Unconfined: корутина не закреплена четко за определенным потоком или пулом потоков. Она запускается в текущем потоке до первой приостановки. После возобновления работы корутина продолжает работу в одном из потоков, который сторого не фиксирован. Разработчики языка Kotlin в обычной ситуации не рекомендуют использовать данный тип.

  • newSingleThreadContext и newFixedThreadPoolContext: позволяют вручную задать поток/пул для выполнения корутины

И мы можем сами задать для корутины диспетчер, передав в функцию launch (а также async) соответствующее значение:

import kotlinx.coroutines.*

suspend fun main() = coroutineScope{

    launch(Dispatchers.Default) {	// явным образом определяем диспетчер Dispatcher.Default
        println("Корутина выполняется на потоке: ${Thread.currentThread().name}")
    }
    println("Функция main выполняется на потоке: ${Thread.currentThread().name}")
}

Dispatchers.Unconfined

Тип Dispatchers.Unconfined запускает корутину в текущем вызывающем потоке до первой приостановки. После возобновления корутина продолжает работу в одном из потоков, который строго не фиксирован. Подобный тип подходит для корутин, которым не требуется интенсивно потреблять время CPU или работать с общими данными, наподобие объектов пользовательского интерфейса. Применим данный тип:

import kotlinx.coroutines.*

suspend fun main() = coroutineScope{

    launch(Dispatchers.Unconfined) {
        println("Поток корутины (до остановки): ${Thread.currentThread().name}")
        delay(500L)
        println("Поток корутины (после остановки): ${Thread.currentThread().name}")
    }

    println("Поток функции main: ${Thread.currentThread().name}")
}

Консольный вывод:

Поток корутины (до остановки): main
Поток функции main: main
Поток корутины (после остановки): kotlinx.coroutines.DefaultExecutor

newSingleThreadContext

newSingleThreadContext вручную запускает поток с указанным именем:

import kotlinx.coroutines.*

suspend fun main() = coroutineScope{

    launch(newSingleThreadContext("Custom Thread")) {
        println("Поток корутины: ${Thread.currentThread().name}")
    }

    println("Поток функции main: ${Thread.currentThread().name}")
}

В данном случае для выполнения корутины будет запускаться поток с именем "Custom Thread". Консольный вывод:

Поток функции main: main
Поток корутины: Custom Thread

В то же время выделенный поток является довольно затратным ресурсом. И в реальном приложении подобый поток следует либо освобождать с помощью функции close(), если он больше не нужен, либо хранить в глобальной переменной и использовать его повторно для подобных задач на протяжении работы приложения.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850