我应该什么时候使我的正常功能暂停功能?

When should I make my normal function suspending function?

使用最新的稳定版 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
}

开头只有一个挂起修饰符,方法块用coroutineScope { }包裹起来。

这还重要吗?哪一个更好?如果函数使用协程或 long 运行ning 函数也应标记为挂起函数,我是否应该只将其设为挂起函数?

我很困惑。我看过最近所有关于协程的讨论,但无法弄清楚这一点。

谁能帮我理解一下?

更新:

所以我最终得到了这样的功能。只是为了确保永远不会在主线程上调用该函数。而且调用函数也不必处处记住this需要在后台线程调用。通过这种方式,我可以使调用函数的事情变得抽象:只做被告知的事情,我不关心你想在哪里处理事情。只需处理并给我结果。 所以函数会自行处理它需要 运行 的地方,而不是在调用函数上。

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
}

这是正确的方法吗?

您想将 CPU 密集型计算卸载到后台线程,这样您的 GUI 线程就不会被阻塞。您不必声明任何暂停功能来实现这一点。这就是您所需要的:

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

以上内容假定您已将 CoroutineScope 界面正确添加到 activity,如其 documentation 中所述。

更好的做法是将 withContext 推入 processList 的定义中,这样您就不会在主线程上犯 运行 的错误。声明如下:

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

这假设您已将字符串到信息逻辑放入

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

挂起的函数是回调的糖分。它允许您以线性方式编写带有回调的代码。如果你的函数内部没有回调调用,也没有调用另一个挂起的函数,那么我认为让你的函数挂起没有意义。除非你想在后台线程中卸载函数内部的工作(挂起的函数并不总是关于后台线程) - 在这种情况下你使用 launch/async 和适当的调度程序。在这种情况下,您可以选择将您的函数包装在 launch/async 中,或者让您的函数暂停并在其中使用 launch/async