Почему запуски области сопрограммы выполняются разными потоками?

#kotlin #asynchronous #kotlin-coroutines

Вопрос:

 import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis

private val started = System.currentTimeMillis()

suspend fun main(args: Array<String>) {
    debug("Start run blocking")
    debug("starting")
    coroutineScope {
        debug("Inside runBlocking")
        val time = measureTimeMillis {
            val one = launch { doSomethingUsefulOne() }
            val two = launch { doSomethingUsefulTwo() }
            debug("awaiting")
//            two.await()
//            one.await()
            debug("Finished")
        }
        debug("time = $time")
    }
    debug("ending")
    debug("End run blocking")
}

suspend fun doSomethingUsefulOne(): Int {
    debug("Inside doSomethingUsefulOne")
    delay(3000L)
    debug("first")
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    debug("Inside doSomethingUsefulTwo")
    delay(1000L)
    debug("second")
    return 29
}
private fun debug(s: String) {
    val elapsed = System.currentTimeMillis() - started
    println("[time elapsed : $elapsed] $s  --  ${Thread.currentThread()}")
}
 

Я работаю с приведенным выше кодом, чтобы изучить поведение сопрограмм. Я получаю ожидаемое поведение следующим образом:

 [time elapsed : 15] Start run blocking  --  Thread[main,5,main]
[time elapsed : 16] starting  --  Thread[main,5,main]
[time elapsed : 34] Inside runBlocking  --  Thread[main,5,main]
[time elapsed : 77] Inside doSomethingUsefulOne  --  Thread[DefaultDispatcher-worker-1,5,main]
[time elapsed : 80] awaiting  --  Thread[main,5,main]
[time elapsed : 80] Finished  --  Thread[main,5,main]
[time elapsed : 80] Inside doSomethingUsefulTwo  --  Thread[DefaultDispatcher-worker-2,5,main]
[time elapsed : 80] time = 46  --  Thread[main,5,main]
[time elapsed : 1089] second  --  Thread[DefaultDispatcher-worker-2,5,main]
[time elapsed : 3088] first  --  Thread[DefaultDispatcher-worker-2,5,main]
[time elapsed : 3089] ending  --  Thread[DefaultDispatcher-worker-2,5,main]
[time elapsed : 3089] End run blocking  --  Thread[DefaultDispatcher-worker-2,5,main]
 

Здесь я не могу следить за тем, как запускались запуски в другом потоке, поскольку он должен использовать основной поток(если я здесь). Кроме того, за пределами сопрограммы работает поток, который выводит последние 2 инструкции, поскольку это должно выполняться основным потоком.

Я хочу знать, в чем я ошибаюсь ?

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

1. Я думаю, что ваше сообщение в журнале не соответствует вашему коду, но в любом случае игнорирование этого запуска является построителем сопрограмм. kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/… Он запускает новую сопрограмму одновременно с остальной частью кода, которая продолжает работать независимо, что означает, что она будет выполняться вне основного потока. для второго вопроса нужно будет просмотреть фактический код и журналы, так как они не совпадают, не знаю, что делать.

2. @ManojMohanty Не понял. Что не так с журналами здесь ?

3. Опубликованный вами код не содержит почти никаких сообщений из опубликованных вами журналов-и наоборот.

4. @MarkoTopolnik исправил журналы. Вы можете проверить сейчас? В первую очередь возникает сомнение в том, почему последние две строки в журналах выполняются диспетчером по умолчанию. Если я правильно понял, то он должен выполняться основным потоком.

5. Почему ваши сообщения журнала противоречат тому, что на самом деле делает код? Это выглядит подозрительно, как будто вы не повторяли запуск, а просто отредактировали опубликованный код.

Ответ №1:

Вы никогда не указываете диспетчера сопрограмм для запуска своих сопрограмм здесь. Это suspend fun main дает вам пустой контекст (без диспетчера), а coroutineScope также не добавляет никакого диспетчера.

Затем вы используете запуск без аргументов, чтобы запустить 2 сопрограммы. Как говорит доктор:

Если в контексте нет ни диспетчера, ни какого-либо другого интерпретатора продолжения, то Диспетчеры.Используется значение по умолчанию.

Это означает, что ваши сопрограммы будут отправлены с помощью диспетчеров.По умолчанию, который предоставляет несколько рабочих потоков в зависимости от количества имеющихся у вас ядер процессора.

Если вы хотите запустить эти сопрограммы в основном потоке, вы можете использовать launch(Dispatchers.Main) их вместо этого (но они доступны только в специальных средах, таких как Android и JavaFX AFAIK).

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

1. согласен, но почему последние 2 инструкции выполняются потоками по умолчанию, а не основными потоками. Они даже выходят за рамки ?

2. @Garrick Поскольку для остальной части кода не указан диспетчер, я думаю, нет причин переключать контекст, чтобы он оставался в Диспетчерах. Рабочие потоки по умолчанию. Вы могли бы использовать withContext для явного переключения на некоторые конкретные потоки. Однако, если вы не используете Android или JavaFX, я думаю, вы не сможете использовать Dispatchers.Main .