协程没有完成,因为它有一个与之关联的异步未完成,如何 运行 独立异步?

Coroutine does not finish because it has an async associated with it that did not finish, how to run async independently?

我只是在玩协程,只是注意到了一个小的不便或者更确切地说是一个很棒的功能。但以下代码永远不会完成:

val job = GlobalScope.launch {
    doSomeWork()
    async {
        while(true) { //if removed job.join finishes in 60 seconds
            delay(60000L)
            doRecurringWork()
        }
    }
}

runBlocking {
    job.join()
}

似乎 job 完成了,但 job.join 等待 async 完成,这本来是不会发生的。

有什么方法可以取消 async 操作与当前协程的链接吗? (运行 另一个工作解决了这个问题,但避免了这个问题)

这里有一些更详细的代码:

val tag="tag"
Log.d(tag,"in main before launch")
val job = GlobalScope.launch {
    Log.d(tag,"in GlobalScope before doSomeWork")
    doSomeWork()
    Log.d(tag,"in GlobalScope after doSomeWork before async")
    async {
        Log.d(tag,"in async before while")
        while(true) { //if removed job.join finishes in 60 seconds
            delay(60000L)
            doRecurringWork()
            Log.d(tag,"in async after doRecurringWork")
        }
        Log.d(tag,"in async after while (die)")
    }
    Log.d(tag,"in GlobalScope after async (die)")
}
Log.d(tag,"in main after launch before runBlocking")
runBlocking {
    Log.d(tag,"in runBlocking before join")
    job.join()
    Log.d(tag,"in runBlocking after join (die)")
}
Log.d(tag,"in main after runBlocking (die)")
    
//log:
//in main before launch
//in main after launch before runBlocking
//in runBlocking before join
//in GlobalScope before doSomeWork
//in GlobalScope after doSomeWork before async
//in async before while
//in GlobalScope after async (die)
//in async after doRecurringWork
//in async after doRecurringWork
//in async after doRecurringWork

注意:我把代码调高了一点,人们回避问题并专注于技术细节。

runBlocking 阻塞主线程,然后 async 块内的 withContext(Dispatchers.Main) 尝试在已经阻塞的主线程上执行代码块,导致死锁。如果您将调度程序从 Main 更改为 IO,那么您的代码将起作用(但它无法访问 UI)。

可以将 runBlocking 用于学习和探索的目的,但不应将其用于其他目的,因为它会破坏协程的全部目的。

Is there any way to unlink an async operation from the current coroutine?

一个coroutine和它的child人之间存在parentchild关系,parentJob只有当所有其child仁已毕。因此,您无法说“考虑 parent 作业已完成,而 child 异步操作仍在 运行'

docs

作业是分层排列的。您的 async 工作是您 launch 工作的 child,并且 parent 在其 children 完成之前无法完成。

这与“结构化并发”背后的整个想法很接近——当您的作业完成时,您知道它实际上已经完成,包括它的所有异步子任务。

如果你想做其他语言做的事情,那么你可以使用 GlobalScope.async 而不是 async。然后你的 parent 工作可以提前 return,完全忘记 async 工作,因为它是兄弟而不是 child。该工作可以 运行 永远,无缘无故地默默消耗资源。

如果您不希望 async 工作永远 运行,那么您应该将其保留为 child 工作,并且在您不想要时取消它再等等。