Изменить 2: Мне кажется, я неправильно понял документацию. Я читаю:
runBlocking
Эту функцию нельзя использовать из сопрограммы. Он предназначен для соединения обычного блокирующего кода с библиотеками, написанными в стиле приостановки, для использования в
main
функциях и в тестах.
Это означает, что я не должен использовать runBlocking()
вообще, кроме main
или тестов. Но теперь я понимаю, что прочитал неправильно, особенно эту часть:
Он разработан, чтобы связать обычный блокирующий код с библиотеками, написанными в режиме приостановки
Похоже, что в этом сценарии необходимо использовать runBlocking .
Однако я думаю, что мне следует полностью изучить тему контекстов, чтобы увидеть, какие контексты лучше всего подходят для перехода к runBlocking в этом случае:
return runBlocking(???????) {
job1.await() + job2.await()
}
Изменить: Ясно, что я сформулировал вопрос плохо, так как все попытки ответить на него упускают из виду фактический вопрос и ограничение, которое я задаю. Итак, давайте попробуем другой подход ...
Это работает:
fun doSomething(): Int {
val job1 = GlobalScope.async { calculateSomething() }
val job2 = GlobalScope.async { calculateSomething() }
return runBlocking {
job1.await() + job2.await()
}
}
suspend fun calculateSomething(): Int {
delay(1000L)
return 13
}
suspend fun calculateSomethingElse(): Int {
delay(2000L)
return 19
}
У меня вопрос: смогу ли я добиться такого же результата?
- Без использования
runBlocking()
. - Без превращения
doSomething()
вsuspend
функцию.
?
Другими словами: есть ли что-нибудь, что я могу поставить вместо ??????
, чтобы выполнить следующую работу?
fun doSomething(): Int {
val job1 = GlobalScope.async { calculateSomething() }
val job2 = GlobalScope.async { calculateSomething() }
return ????????? {
job1.?????() + job2.?????()
}
}
suspend fun calculateSomething(): Int {
delay(1000L)
return 13
}
suspend fun calculateSomethingElse(): Int {
delay(2000L)
return 19
}
У меня есть небольшой служебный метод, который запускает любую заданную внешнюю команду и возвращает ее результат (то есть небольшую оболочку вокруг Java Process API):
class RunningCommand(private val proc: Process) {
fun waitFor(): String {
proc.outputStream.close()
var output = ""
val outputRdr = thread { output = proc.inputStream.bufferedReader().use { it.readText() } }
var error = ""
val errorRdr = thread { error = proc.errorStream.bufferedReader().use { it.readText() } }
proc.waitFor()
outputRdr.join()
errorRdr.join()
if (proc.exitValue() != 0) {
throw RuntimeException("Command returned non-zero status: $output$error")
}
return output
}
}
Этот код работает правильно. Однако он создает два дополнительных потока для каждого выполнения команды. Я хотел избежать этого, переключившись на сопрограммы. Я смог это сделать, но мне пришлось использовать runBlocking
:
class RunningCommand(private val proc: Process) {
fun waitFor(): String {
proc.outputStream.close()
var output = ""
val outputRdr = GlobalScope.async { output = proc.inputStream.bufferedReader().use { it.readText() } }
var error = ""
val errorRdr = GlobalScope.async { error = proc.errorStream.bufferedReader().use { it.readText() } }
proc.waitFor()
runBlocking {
outputRdr.await()
errorRdr.await()
}
if (proc.exitValue() != 0) {
throw RuntimeException("Command returned non-zero status: $output${error}")
}
return output
}
}
Этот код также работает, но я читал, что runBlocking
следует использовать только в main()
методах и тестах, то есть не предназначено для использования таким образом. Если взглянуть на его реализацию, это выглядит ужасно и действительно похоже на то, что не хотелось бы вызывать повторно из какого-то служебного метода.
Итак, мой вопрос: как еще я должен преодолеть разрыв между блокирующим кодом и сопрограммами? Или, другими словами, как правильно ждать suspend
функции из кода, отличного от suspend
?
Или просто мой дизайн неправильный, и чтобы использовать сопрограммы где-нибудь в дальнейшем, мне нужно создать main()
метод runBlocking
и, по сути, всегда находиться внутри некоторой области сопрограмм?