Size: a a a

Kotlin Community

2020 April 17

NY

Nikita Yatskivskiy in Kotlin Community
Ребят, я правильно понимаю, что оба этих варианты равнозначны? Что новый скоуп станет дочерним для скоупа, в которым мы вызвали suspend-функцию doSomeWork.
private suspend fun doSomeWork() = coroutineScope {
launch {
 delay(5000)
}
}

private suspend fun doSomeWork() = CoroutineScope(coroutineContext).launch {
delay(5000)
}
источник

RE

Roman Elizarov in Kotlin Community
Нет. Совсем не развнозначны
источник

RE

Roman Elizarov in Kotlin Community
В первом случае жизненный цикл запущенной через launch корутины ограничен жизненным циклом вызова функции doSomeWork. Не может так получиться, что doSomeWork закончился, а корутина продолжает работать. Есть строгая гарантия что при завершении doSomeWork (по любой причине) ничего не утекло и никаких фоновых процессов не осталось. Можно, например, спокойно вызывать doSomeWork в цикле и не бояться что закончится память.
источник

MR

Max Rovkin in Kotlin Community
Alexander Nozik
А вы руками детей отменяете что ли?
Да
источник

RE

Roman Elizarov in Kotlin Community
Во втором случае такой гарантии нет. Жизненный цикл запущенной корутины привязан к какой-то запущенной выше по стеку корутине. Никогда не пишите код как во втором случае. Это закончится очень плохо. Например, если вызывать так написанный doSomeWork в цикле, то дело закончится OutOfMemoryError, так как каждый успешный doSomeWork будет утекать работающую в фоне корутину.
источник

MR

Max Rovkin in Kotlin Community
Alexander Nozik
А вы руками детей отменяете что ли?
Или я не правильно вопрос понял
источник

AN

Alexander Nozik in Kotlin Community
А заем? Отменяете родителя и отмена детей происходит автоматически
источник

NY

Nikita Yatskivskiy in Kotlin Community
Roman Elizarov
Во втором случае такой гарантии нет. Жизненный цикл запущенной корутины привязан к какой-то запущенной выше по стеку корутине. Никогда не пишите код как во втором случае. Это закончится очень плохо. Например, если вызывать так написанный doSomeWork в цикле, то дело закончится OutOfMemoryError, так как каждый успешный doSomeWork будет утекать работающую в фоне корутину.
У меня такая задача, что нужно подписаться на канал (через consumeEach). И чтобы эта подписка была привязана к скоупу, в котором этот doSomeWork вызвали. Но при этом сам скоуп (viewModelScope) прокидывать не хочется. Сейчас делаю вторым спобом. Поскольку циклов нет и просто разово выполняется подписка.
источник

NY

Nikita Yatskivskiy in Kotlin Community
То есть мне нужно, чтобы корутина жила, пока живёт скоуп viewModelScope. Значит, второй способ подходит? Или есть более оптимальный?
источник

RE

Roman Elizarov in Kotlin Community
Не пишите так код, пожалуйста. Это очень плохой стиль. Прокидывайте нужный скоуп явно
fun CoroutineScope.doSomeWork() = launch { ... }
источник

VP

Vladimir Petrakovich in Kotlin Community
Nikita Yatskivskiy
То есть мне нужно, чтобы корутина жила, пока живёт скоуп viewModelScope. Значит, второй способ подходит? Или есть более оптимальный?
Передавать scope явно
источник

RE

Roman Elizarov in Kotlin Community
Тогда читающему ваш код будет сразно понятно, что функция doSomeWork запускает какую-то корутину и надо быть осторожным (не вызывать её в бесконечном цикле, например)
источник

RE

Roman Elizarov in Kotlin Community
И так как она не suspend, то опять же сразу понятно что сам doSomeWork завершается быстро и ничего не ждет
источник

RE

Roman Elizarov in Kotlin Community
Ибо если я вижу в коде suspend fun doSomeWork() то я ожидаю что она делает какую-то работу и возвращается только когда эта работа закончена (документацию и реализацию никто читать не будет)
источник

RE

Roman Elizarov in Kotlin Community
Ну и лучше её называть, конечно, не doSomeWork, а как-нибудь типа launchSomeWork
fun CoroutineScope.launchSomeWork() = launch { ... }
источник

NY

Nikita Yatskivskiy in Kotlin Community
Так, давайте срезюмируем на более конкретном примере))

private val channel = BroadcastChannel<Int>(1)

private fun bind(scope: CoroutineScope) = scope.launch {
       channel.openSubscription()
               .consumeEach { println(it) }
}


1. Нет suspend, следовательно пользователь нашей функции сразу видит, что долгой работы никакой не будет.
2. Мы явно передали скоуп, показывая, что нашу функцию не стоит вызывать в цикле, так как каждый созданный скоуп… А можно поподробнее про OutOfMemoryError? Потому что каждый созданный скоуп - это дороже по памяти, чем просто вызванная suspend-функция?
источник

AN

Alexander Nozik in Kotlin Community
Nikita Yatskivskiy
Так, давайте срезюмируем на более конкретном примере))

private val channel = BroadcastChannel<Int>(1)

private fun bind(scope: CoroutineScope) = scope.launch {
       channel.openSubscription()
               .consumeEach { println(it) }
}


1. Нет suspend, следовательно пользователь нашей функции сразу видит, что долгой работы никакой не будет.
2. Мы явно передали скоуп, показывая, что нашу функцию не стоит вызывать в цикле, так как каждый созданный скоуп… А можно поподробнее про OutOfMemoryError? Потому что каждый созданный скоуп - это дороже по памяти, чем просто вызванная suspend-функция?
Разумеется дороже. Это не тред, конечно, но тоже сильно не бесплатно.
источник

RE

Roman Elizarov in Kotlin Community
Попробуйте и посмотрите сами https://pl.kotl.in/2LKVdT5Gb
источник

RE

Roman Elizarov in Kotlin Community
Сравните это вот с таким кодом, который просто бесконечно долго работает, но память у него никогда не закончится: https://pl.kotl.in/oPNLa0VG4
источник

MR

Max Rovkin in Kotlin Community
Alexander Nozik
А заем? Отменяете родителя и отмена детей происходит автоматически
Не понимаю, как это связано
источник