#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
.