Ответ таков: Coroutine не знает о сетевых вызовах или операциях ввода-вывода.Вы должны написать код в соответствии с тем, что вы хотите, заключив тяжелую работу в различные сопрограммы, чтобы они могли выполняться одновременно, потому что поведение по умолчанию является последовательным.
Например:
suspend fun doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here (maybe I/O)
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here (maybe I/O), too
return 29
}
fun main(args: Array<String>) = runBlocking<Unit> {
val time = measureTimeMillis {
val one = doSomethingUsefulOne()
val two = doSomethingUsefulTwo()
println("The answer is ${one + two}")
}
println("Completed in $time ms")
}
выдаст что-то вроде этого:
The answer is 42
Completed in 2017 ms
, а doSomethingUsefulOne () и doSomethingUsefulTwo () будут выполняться последовательно.Если вы хотите одновременное выполнение, вы должны вместо этого написать:
fun main(args: Array<String>) = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
, который будет выдавать:
The answer is 42
Completed in 1017 ms
, поскольку doSomethingUsefulOne () и doSomethingUsefulTwo () будут выполняться одновременно.
Источник: https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#composing-suspending-functions
ОБНОВЛЕНИЕ: О том, где исполняются сопрограммы, мы можем прочитать в руководстве по проекту github https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#thread-local-data:
Иногда это удобноиметь возможность передавать некоторые локальные данные потока, но для сопрограмм, которые не связаны с каким-либо конкретным потоком, трудно достичь этого вручную без написания большого количества шаблонов.
Для ThreadLocal asContextElementФункция расширения здесь для спасения.Он создает дополнительный элемент контекста, который сохраняет значение заданного ThreadLocal и восстанавливает его каждый раз, когда сопрограмма переключает свой контекст.
Его легко продемонстрировать в действии:
val threadLocal = ThreadLocal<String?>() // declare thread-local variable
fun main(args: Array<String>) = runBlocking<Unit> {
threadLocal.set("main")
println("Pre-main, current thread: ${Thread.currentThread()}, threadlocal value: '${threadLocal.get()}'")
val job = launch(Dispatchers.Default + threadLocal.asContextElement(value = "launch")) {
println("Launch start, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
yield()
println("After yield, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
}
job.join()
println("Post-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")
}
В этом примере мы запускаем новую сопрограмму в фоновом пуле потоков с помощью Dispatchers.Default, поэтому она работает в потоках, отличных от пула потоков, но все равно имеет значение локальной переменной потока, которое мыуказывается с помощью threadLocal.asContextElement (value = "launch"), независимо от того, в каком потоке выполняется сопрограмма.Таким образом, вывод (с отладкой):
Pre-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main'
Launch start, current thread: Thread[CommonPool-worker-1 @coroutine#2,5,main], thread local value: 'launch'
After yield, current thread: Thread[CommonPool-worker-2 @coroutine#2,5,main], thread local value: 'launch'
Post-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main'