Kotlin协程-如何在后台运行并在调用方线程中使用结果?

时间:2019-12-26 09:28:59

标签: kotlin kotlin-coroutines

主要思想是拥有非挂起功能runInBackgroundAndUseInCallerThread(callback: (SomeModel) -> Unit),该功能在后台(另一个线程)在后台异步运行某些工作,并且在工作完成后-在调用者线程(启动runInBackgroundAndUseInCallerThread的线程中运行回调) )。

下面我写了一个示例代码,但是我不确定它的正确性以及是否完全可能。用println("1/2/3/...")标记了所需的呼叫顺序。 getDispatcherFromCurrentThread-如果可以实现此功能,则可以使用解决方案,但我不知道如何实现,完全做到这一点是正确的。

因此,请不要将其视为唯一的解决方案。

import kotlinx.coroutines.*
import kotlin.concurrent.thread

fun main() {
    println("1")
    runInBackgroundAndUseInCallerThread {
        println("4")
        println("Hello ${it.someField} from ${Thread.currentThread().name}") // should be "Hello TestField from main"
    }
    println("2")
    thread(name = "Second thread") {
        runInBackgroundAndUseInCallerThread {
            println("5")
            println("Hello ${it.someField} from ${Thread.currentThread().name}") // should be "Hello TestField from Second thread"
        }
    }
    println("3")
    Thread.sleep(3000)
    println("6")
}

fun runInBackgroundAndUseInCallerThread(callback: (SomeModel) -> Unit) {
    val dispatcherFromCallerThread: CoroutineDispatcher = getDispatcherFromCurrentThread()
    CoroutineScope(Dispatchers.IO).launch {
        val result: SomeModel = getModelResult()
        launch(dispatcherFromCallerThread) { callback(result) }
    }
}

data class SomeModel(val someField: String)

suspend fun getModelResult(): SomeModel {
    delay(1000)
    return SomeModel("TestField")
}

fun getDispatcherFromCurrentThread(): CoroutineDispatcher {
    // TODO: Create dispatcher from current thread... How to do that?
}

3 个答案:

答案 0 :(得分:0)

除非线程被设计为可以作为调度程序使用,否则没有一种通用的方法可以使它成为调度程序。 想到的唯一方法是重新进入runBlocking并在现有线程中创建一个事件循环,但是它将阻止所有非协程代码在该线程上执行直到完成。

这最终看起来像:

fun runInBackgroundAndUseInCallerThread(callback: (SomeModel) -> Unit) {
    callback(runBlocking(Dispatchers.IO) {
        getModelResult()
    })
}

答案 1 :(得分:0)

dispatcher确实是coroutineContext,在scope中使用时很有意义 因此,如果要将父作用域的调度程序传递到子作用域,则可以这样做。

GlobalScope.launch {
        val dispatcher = this.coroutineContext
        CoroutineScope(dispatcher).launch {

        }
}

因此getDispatcherFromCurrentThread应该是这样。

fun getDispatcherFromCurrentThread(scope: CoroutineScope): CoroutineContext {
    return scope.coroutineContext
}

GlobalScope.launch {
            val dispatcher = getDispatcherFromCurrentThread(this)
            CoroutineScope(dispatcher).launch {

            }
    }

答案 2 :(得分:0)

  

在后台(另一个线程)中异步运行一些工作,并且在工作完成后-在调用者线程中运行回调

首先尝试回答以下问题:正在进行后台工作时调用线程应该做什么

很显然,它无法继续执行代码的下一行,该代码应该在完成后台工作之后运行。

您也不希望它阻止并等待。

那么应该运行什么代码?

唯一合理的答案如下:调用线程应在其最高执行级别(入口点函数)运行无限事件循环。您问题中的代码应在提交给事件循环的事件处理程序中。在您要等待后台工作的那一刻,处理程序必须返回,以便线程可以继续处理其他事件,并且必须在后台工作完成时准备好另一个处理程序提交。与您的callback相对应的第二个处理程序称为 continuation ,Kotlin会自动提供它。实际上,您不需要自己的回调。

但是,现在出现了最敏感的问题:如何将延续提交给事件循环?这不是您可以抽象的东西,必须使用特定于所讨论事件循环的一些API

这就是为什么Kotlin具有Dispatcher的概念。它捕获了将连续性分配到所需线程的特定案例问题。您似乎想要解决此问题,而无需编写专门用于每个特定事件循环的调度程序,不幸的是,这是不可能的。