Использует ли Kotlin delay внутренний диспетчер для разблокировки вызывающего потока?

#kotlin #kotlin-coroutines

#kotlin #kotlin-сопрограммы

Вопрос:

Это некоторый тестовый код, который я использую для изучения сопрограмм kotlin. Код работает как ожидалось и для вывода суммы требуется около 1 секунды, но теперь, если я заменю delay (1000) блокирующим вызовом, таким как сетевой запрос, то коду потребуется около 10 секунд для вывода суммы (каждый вызов занимает около 1 секунды), но если я оберну сетевой вызов в withContext и использую диспетчер ввода-вывода, для вывода суммы потребуется 1 секунда, потому что он выполняется в другом потоке. Использует ли функция задержки какой-то диспетчер для разблокировки потока?

 
suspend fun asyncDoubleFn(num: Int): Int {
    delay(1000)
    return num * 2
}


fun main() = runBlocking {
    launch {
        val tt = measureTimeMillis {
            val results = mutableListOf<Deferred<Int>>()
            for (num in 0..10) {
                val result = async { asyncDoubleFn(num   1) }
                results.add(result)
            }
            val sum = results.map { it.await() }.reduce { acc, i -> acc   i }
            println("[SUM]: $sum")
        }

        println("[TT]: $tt")
    }


    launch {
        println("Another coroutine")
    }

    println("Main Code")


}
  

Комментарии:

1. Источник: github.com/Kotlin/kotlinx.coroutines/blob/master/common/…

2. Спасибо! Я искал небольшое подробное объяснение, поскольку я новичок в этой концепции, но знаком с JS async await (в основном использую интерфейс JS). В основном я хочу понять, можем ли мы использовать блокирующий API, такой как java.net . URL и запустите его в режиме приостановки.

3. Вы можете взглянуть на suspendCoroutine . Я недостаточно знаком с деталями, чтобы полностью объяснить, как это работает, но на KotlinConf был хороший разговор об этом .

Ответ №1:

Использует ли функция delay какой-либо диспетчер для разблокировки потока?

Не только delay . Все приостанавливаемые функции взаимодействуют с диспетчером.

Вопрос, который вы должны задать: «Какой диспетчер здесь главный?»

И ответ таков: runBlocking устанавливает свой собственный диспетчер, который отправляет данные потоку, в котором он вызывается. Оба launch и async в вашем коде наследуют это.

Имея это в виду:

Если я заменю delay (1000) блокирующим вызовом, таким как сетевой запрос, то коду потребуется около 10 секунд, чтобы вывести сумму (каждый вызов занимает около 1 секунды)

Каждый блокирующий вызов будет привязан к одному потоку диспетчера. Он не сможет выполнять какую-либо другую работу, будучи заблокированным.

но если я оберну сетевой вызов в withContext и использую IO диспетчер, для вывода суммы потребуется 1 секунда, потому что она выполняется в другом потоке

Да, это меняет диспетчера, проблема решена.

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

Комментарии:

1. Спасибо за подробный ответ, это устраняет большую часть моего замешательства, вы сказали, что delay приостанавливает текущую сопрограмму и возвращает управление диспетчеру, использует ли delay какое-то планирование очереди задач для запуска кода по истечении времени задержки, работает ли это как setTimeout в JS?.

2. Это работает практически точно так. runBlocking Диспетчер устанавливает очередь событий и цикл событий, который разряжает очередь и обрабатывает события. Точно так же, как Android looper, цикл событий Swing или цикл событий JS.