5

使用 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 { }.

这还有关系吗?哪一个更好?如果它使用协程或长时间运行的函数也应该标记为挂起函数,我应该只制作函数挂起函数吗?

我很困惑。我看过最近关于协程的所有谈话,但不能清楚这一点。

谁能帮我理解这一点?

更新:

所以我最终有了这样的功能。只是为了确保永远不会在主线程上调用该函数。并且调用函数不必在任何地方都记住需要在后台线程上调用它。通过这种方式,我可以将调用函数的东西抽象化:只要按照指示做,我不在乎你想在哪里处理这些东西。只需处理并给我结果。因此,它自己负责它需要运行的位置而不是调用函数。

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
}

这是正确的方法吗?

4

2 回答 2

3

您希望将 CPU 密集型计算卸载到后台线程,这样您的 GUI 线程就不会被阻塞。您不必声明任何挂起函数来实现这一点。这就是你需要的:

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
于 2018-11-09T17:40:43.337 回答
2

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

于 2018-11-10T11:48:18.737 回答