Когда я должен сделать свою обычную функцию приостановки функции? - PullRequest
0 голосов
/ 09 ноября 2018

С новой стабильной версией сопрограмм kotlin я пытался реализовать одну из функциональных возможностей моего приложения, используя ее. Но я немного запутался.

По сути, у меня есть функция, которая выполняет некоторую работу со списком элементов. Это занимает около 700-1000 мс.

fun processList(list : ArrayList<String>) : ArrayList<Info> {

    val result = mutableListOf<Info>()

    for (item in list) {
        // Process list item
        // Each list item takes about ~ 15-20ms
        result.add(info) // add processed info to result
    }

    return result  // return result
}

Теперь я хотел, чтобы это не блокировало основной поток. Поэтому я запускаю эту функцию внутри блока запуска, чтобы она не блокировала основной поток.

coroutineScope.launch(Dispatchers.Main) {
    val result = processList(list)
}

Это просто отлично работает.

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

suspend fun processList(list : ArrayList<String>) : ArrayList<Info> = coroutineScope {

    val result = mutableListOf<Info>()

    for (item in list) {
        // Process list item
        // Each list item takes about ~ 15-20ms
        result.add(info) // add processed info to result
    }

    return result  // return result
}

В начале есть только модификатор suspend, а блок метода заключен в coroutineScope { }.

Это все еще имеет значение? Какой из них лучше? Должен ли я сделать функцию приостановки функции, только если она использует сопрограмму, ИЛИ долго выполняющиеся функции также должны быть помечены как функция приостановки?

Я в замешательстве. Я смотрел все недавние разговоры о сопрограммах, но не смог прояснить этот момент.

Может кто-нибудь помочь мне понять это?

UPDATE:

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

suspend fun processList(list : ArrayList<String>) : ArrayList<Info> = coroutineScope {

    val result = mutableListOf<Info>()

    launch(Dispatchers.Default) {
        for (item in list) {
            // Process list item
            // Each list item takes about ~ 15-20ms
            result.add(info) // add processed info to result
        }
    }.join() // wait for the task to complete on the background thread

    return result  // return result
}

Это правильный путь?

Ответы [ 2 ]

0 голосов
/ 10 ноября 2018

Приостановленная функция - сахар над обратным вызовом. Это позволяет вам писать код с обратными вызовами линейным способом. Если у вас нет вызовов обратного вызова внутри вашей функции и нет вызовов других приостановленных функций, то я не вижу смысла делать вашу функцию приостановленной. Если вы не хотите разгрузить работу внутри своей функции в фоновом потоке (приостановленные функции не всегда связаны с фоновыми потоками) - в этом случае вы используете launch/async с соответствующим диспетчером. В этом случае вы можете выбрать либо обернуть вашу функцию в launch/async, либо сделать ее приостановленной и использовать launch/async внутри нее.

0 голосов
/ 09 ноября 2018

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

myActivity.launch {
    val processedList = withContext(Default) { processList(list) }
    ... use processedList, you're on the GUI thread here ...
}

Вышеприведенное предполагает, что вы правильно добавили интерфейс CoroutineScope к своей деятельности, как объяснено в документации .

Лучше было бы вставить withContext в определение processList, чтобы вы не допустили ошибку при запуске его в главном потоке. Объявите это следующим образом:

suspend fun processList(list: List<String>): List<Info> = withContext(Default) {
    list.map { it.toInfo() }
}

Предполагается, что вы поместили логику строки в информацию в

fun String.toInfo(): Info = // whatever it takes
...