Почему сопрограммы Kotlin запускаются в одном потоке последовательно? - PullRequest
3 голосов
/ 15 апреля 2019

Я думал, что вызов функции "suspend" из сопрограммного контекста с использованием launch делает вызов асинхронным. Но в приведенном ниже примере я вижу, что 2 вызова метода placeOrder не выполняются в одном потоке один за другим. В чем моя ошибка?

import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.File

fun main() = runBlocking {
    t("1")
    launch {
        t("2")
        placeOrder("C:\\Users")
        t("3")
    }
    launch {
        t("12")
        placeOrder("C:\\Program Files")
        t("13")
    }
    t("4")
}


fun t(s: String) {
    val currentThread = Thread.currentThread()
    println(s + ": " + currentThread.name + " " +     currentThread.id)
}

suspend fun placeOrder(d:String): String {
    t("placeOrder $d")
    val user = createUser(d) // asynchronous call to user service
    val order = createOrder(user) // asynchronous call to order service
    t("placeOrder $d finished")
    return order
}

suspend fun createUser(d:String): String {
    t("createUser $d")
    val toString = File(d).walk().map {
        it.length()
    }.sum().toString()
    t("createUser $d finished")
    return toString
}

suspend fun createOrder(user: String): String {
    t("createOrder $user")
    val toString = File("C:\\User").walk().map {
        it.length()
    }.sum().toString()
    t("createOrder $user finished")
    return toString
}

Выход:

1: main 1
4: main 1
2: main 1
placeOrder C:\Users: main 1
createUser C:\Users: main 1
createUser C:\Users finished: main 1
createOrder 1094020270277: main 1
createOrder 1094020270277 finished: main 1
placeOrder C:\Users finished: main 1
3: main 1
12: main 1
placeOrder C:\Program Files: main 1
createUser C:\Program Files: main 1
createUser C:\Program Files finished: main 1
createOrder 5651227104: main 1
createOrder 5651227104 finished: main 1
placeOrder C:\Program Files finished: main 1
13: main 1

Ответы [ 2 ]

2 голосов
/ 15 апреля 2019

Вместо написания приостановленного ввода-вывода вы написали блокирующий ввод-вывод:

File(d).walk().map {
    it.length()
}

Ваши функции фактически никогда не приостанавливаются, и вместо этого они блокируют один поток, связанный с их диспетчером runBlocking.

Выне дал вашим сопрограммам возможности для одновременного выполнения.

Если вы примените withContext(IO) { ... } к приведенному выше коду, вы получите параллелизм, но из простого старого типа Java, когда несколько потоков блокируются в операциях ввода-вывода вместе.

1 голос
/ 15 апреля 2019

Причина этого поведения двоякая:

  1. Все ваши сопрограммы выполняются в области действия runBlocking, которая является однопоточным циклом событий.Таким образом, это означает, что используется только один поток, если не указан другой контекст.(launch(Dispatchers.IO) в качестве примера)
  2. Даже в этом случае сопрограммы могли бы чередоваться, за исключением того, что ваши сопрограммы действительно вызывают функции приостановки, которые фактически должны приостанавливаться.Это означает, что это обычный обычный последовательный вызов функции.Если в ваши функции включен вызов yield() или delay(..), вы увидите, что сопрограммы чередуются при выполнении.
...