Почему бы не использовать GlobalScope.launch? - PullRequest
0 голосов
/ 23 января 2019

Я читал, что использование Globalscope крайне не рекомендуется, здесь .

У меня есть простой вариант использования.Для каждого полученного мной сообщения кафки (скажем, списка идентификаторов) я должен разделить его и одновременно запустить службу отдыха и подождать, пока это будет сделано, и продолжить выполнение других синхронных задач.В этом приложении больше нет ничего, что требует сопрограммы.В этом случае, могу ли я просто сойти с рук?

Примечание: это не приложение для Android.Это просто потоковый процессор kafka, работающий на стороне сервера.Это эфемерное приложение без документирования в контейнерах (Docker), работающее в Kubernetes (если хотите, совместимое с Buzzword)

Ответы [ 3 ]

0 голосов
/ 24 января 2019

В вашей ссылке указано:

В коде приложения обычно следует использовать CoroutineScope, определяемый приложением, используя async или launch в случае GlobalScope очень обескуражен.

Мой ответ на этот вопрос.

Вообще говоря GlobalScope может быть плохой идеей, потому что это не связано с какой-либо работой.Вы должны использовать его для следующих целей:

Глобальная область используется для запуска сопрограмм верхнего уровня, которые работают в течение всего времени жизни приложения и не отменяются преждевременно.

Это не похоже на ваш вариант использования.


Для получения дополнительной информации в официальных документах есть отрывок по адресу https://kotlinlang.org/docs/reference/coroutines/basics.html#structured-concurrency

Есть еще что-то, что можно пожелатьпрактическое использование сопрограмм.Когда мы используем GlobalScope.launch, мы создаем сопрограмму верхнего уровня.Несмотря на то, что он легкий, он все же потребляет некоторые ресурсы памяти во время работы.Если мы забываем сохранить ссылку на недавно запущенную сопрограмму, она все равно запускается.Что, если код в сопрограмме зависает (например, мы ошибочно задерживаемся слишком долго), что, если мы запустили слишком много сопрограмм и исчерпали память?Необходимость вручную сохранять ссылки на все запущенные сопрограммы и присоединяться к ним подвержена ошибкам.

Существует лучшее решение.Мы можем использовать структурированный параллелизм в нашем коде.Вместо запуска сопрограмм в GlobalScope, как мы обычно делаем с потоками (потоки всегда глобальны), мы можем запускать сопрограммы в конкретной области выполняемой нами операции.

В нашем примере мыесть основная функция, которая превращается в сопрограмму с помощью runBlocking конструктора сопрограмм.Каждый компилятор сопрограмм, включая runBlocking, добавляет экземпляр CoroutineScope в область действия своего блока кода.Мы можем запускать сопрограммы в этой области без явного присоединения к ним, потому что внешняя сопрограмма (runBlocking в нашем примере) не завершится, пока не завершатся все сопрограммы, запущенные в этой области.Таким образом, мы можем упростить наш пример:

import kotlinx.coroutines.*

fun main() = runBlocking { // this: CoroutineScope
    launch { // launch new coroutine in the scope of runBlocking   
        delay(1000L)   
        println("World!")    
    }   
    println("Hello,")  
}

Так что, по сути, это не рекомендуется, потому что это заставляет вас сохранять ссылки и использовать join, чего можно избежать с помощью структурированной параллелизм. (см. пример кода выше.) В статье рассматриваются многие тонкости.

0 голосов
/ 24 января 2019

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

Вот пример:

/* I don't know Kafka, but let's pretend this function gets 
 * called when you receive a new message
 */
suspend fun onMessage(Message msg) {
    val ids: List<Int> = msg.getIds()    

    val jobs = ids.map { id ->
        GlobalScope.launch { restService.post(id) }
    }

    jobs.joinAll()
}

Если один из вызовов restService.post(id) завершается неудачно с исключением,Пример немедленно сбросит исключение, и все задания, которые еще не были завершены, будут утечки.Они будут продолжать выполняться (возможно, на неопределенный срок), и если они потерпят неудачу, вы не будете знать об этом.

Чтобы решить эту проблему, вам нужно настроить свои сопрограммы.Вот тот же пример без утечки:

suspend fun onMessage(Message msg) = coroutineScope {
    val ids: List<Int> = msg.getIds()    

    ids.forEach { id ->
        // launch is called on "this", which is the coroutineScope.
        launch { restService.post(id) }
    }
}

В этом случае, если один из вызовов restService.post(id) завершится неудачно, все другие незавершенные сопрограммы внутри области сопрограмм будут отменены.Когда вы выходите из области действия, вы можете быть уверены, что не пропустили никаких сопрограмм.

Кроме того, поскольку coroutineScope будет ждать, пока все дочерние сопрограммы не будут выполнены, вы можете сбросить вызов jobs.joinAll().

Примечание: при написании функции, которая запускает некоторые сопрограммы, соглашение позволяет вызывающей стороне определять область сопрограммы с помощью параметра приемника.Выполнение этого с помощью функции onMessage может выглядеть следующим образом:

fun CoroutineScope.onMessage(Message msg): List<Job> {
    val ids: List<Int> = msg.getIds()    

    return ids.map { id ->
        // launch is called on "this", which is the coroutineScope.
        launch { restService.post(id) }
    }
}
0 голосов
/ 24 января 2019

По документам использование асинхронного или запуска на экземпляре GlobalScope крайне нежелательно, код приложения обычно должен использовать определяемый приложением CoroutineScope.

Если мы посмотрим наИз определения GlobalScope мы увидим, что он объявлен как объект :

object GlobalScope : CoroutineScope { ... }

объект представляет один статический экземпляр (Singleton) Kotlin / JVM статическая переменная возникает, когда класс загружается JVM, и умирает, когда класс выгружается.При первом использовании GlobalScope он будет загружен в память и будет оставаться там до тех пор, пока не произойдет одно из следующих событий:

  1. класс выгружен
  2. JVM выключается
  3. процесс умирает

Таким образом, он будет занимать некоторое количество памяти во время работы вашего серверного приложения.Даже если ваше серверное приложение завершено, но процесс не уничтожен, запущенная сопрограмма все еще может работать и потреблять память.

Запуск новой сопрограммы из глобальной области с помощью GlobalScope.async или GlobalScope.launch создастсопрограмма верхнего уровня " независимо ".

Механизм, обеспечивающий структуру сопрограмм, называется структурированный параллелизм .Давайте посмотрим, какие преимущества имеет структурированный параллелизм по сравнению с глобальными областями :

  • Область действия обычно отвечает за дочерние сопрограммы, и их время жизни привязановремя жизни области.
  • Область может автоматически отменять дочерние сопрограммы, если что-то идет не так или если пользователь просто передумает и решает отменить операцию.
  • Область автоматически ожидает завершениявсех детских сопрограмм.Поэтому, если область соответствует сопрограмме, родительская сопрограмма не завершится, пока не будут завершены все сопрограммы, запущенные в этой области.

При использовании GlobalScope.async нет структуры, котораясвязывает несколько сопрограмм с меньшей областью .Сопрограммы, запущенные из глобальной области 1060 *, все независимы ;срок их службы ограничен только временем жизни всего приложения.Можно сохранить ссылку на сопрограмму, запущенную из глобальной области видимости, и дождаться ее завершения или явно отменить ее, но это не произойдет автоматически, как это было бы с структурированным .Если мы хотим отменить все сопрограммы в области действия с структурированным параллелизмом , нам нужно только отменить родительскую сопрограмму, и это автоматически распространит отмену на все дочерние сопрограммы.

Если вы неНе требуется привязывать сопрограмму к определенному объекту времени жизни, и вы хотите запустить независимую сопрограмму верхнего уровня, которая работает в течение всего времени жизни приложения и не отменяется преждевременно, и вы не хотите использовать преимущества структурированный параллелизм , затем используйте global scopes .

...