大佬教程收集整理的这篇文章主要介绍了在 Kotlin 中创建子协程范围,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
我想知道是否有或多或少的标准方法来创建协程上下文/范围,例如:
coroutInescope() 正是我所需要的,它创建了一个子作用域,但它并不简单地将它返回给调用者——我们需要传递一个 lambda 并且协程生命周期仅限于这个 lambda 的执行。另一方面,CoroutInescope() 工厂创建了一个长期运行的作用域,我可以存储以备后用,但它与当前的协程无关。
我能够手动创建这样的范围:
suspend fun createChildCoroutInescope(): CoroutInescope {
val ctx = coroutIneContext
return CoroutInescope(ctx + Job(ctx.job))
}
@H_696_11@@H_801_21@
乍一看,它似乎完全符合我的需要。它是否相当于 coroutInescope()@H_696_11@ 的作用,或者我的解决方案在某种程度上不完整,我应该执行一些额外的任务?我试图阅读coroutInescope()@H_696_11@的源代码,但它相当复杂。是否有更简单或更标准的方法来创建子作用域?
另外,这是否被认为是一种不好的做法或反模式?我只是担心如果已经没有这样一个简单的函数,那么可能是有原因的,我不应该真正以这种方式使用协程。
用例(长话)
通常,当我实现某种可以异步安排后台操作的长时间运行的服务时,我认为需要这样做:
class Myservice {
fun scheduleSoMetask() {
// start task in the BACkground
// return immediately
}
}
@H_696_11@@H_801_21@
使用协程有几种可能性:
-
GlobalScope@H_696_11@,但很糟糕。
-
使 scheduleSoMetask()@H_696_11@ 可挂起并使用当前协程运行后台任务。在许多情况下,我认为这种方法并不合适:
- 后台任务由调用者而不是服务本身“拥有”。如果我们例如停止服务,后台任务仍将运行。
- 它要求调度功能是可挂起的。我认为这是错误的,因为我真的不明白为什么不允许某些 Java 代码或协程上下文之外的代码在我的服务中安排任务。
-
给我的服务一个定义的生命周期,在停止/销毁时使用 CoroutInescope()@H_696_11@ 和 cancel()@H_696_11@ 创建范围。这很好,但我认为我们仍然可以从协程的结构化并发中受益,所以对我来说,我的服务是分离的。
例如,我们有一个文件下载服务,它由(拥有)其他服务组成,包括数据缓存服务。使用start()@H_696_11@/stop()@H_696_11@服务的典型方法,我们需要手动控制生命周期,并且很难正确处理故障。协程让它变得更容易:如果缓存服务崩溃,它会自动传播到下载服务;如果下载服务需要停止,它只是取消它的协程,它可以确保它不会泄漏它的任何子组件。因此,对我而言,在设计由多个小型服务组成的应用程序时,协程的结构化并发可能非常有用。
我目前的方法是这样的:
class Myservice {
private lateinit var coroutIne : CoroutInescope
suspend fun start() {
coroutIne = createChildCoroutInescope() + CoroutInename("Myservice")
}
fun stop() {
coroutIne.cancel()
}
fun scheduleSoMetask() {
coroutIne.launch {
// do something
}
}
}
@H_696_11@@H_801_21@
或者:
class Myservice(
private val coroutIne: CoroutInescope
) {
companion object {
suspend fun start() = Myservice(createChildCoroutInescope())
}
}
@H_696_11@@H_801_21@
通过这种方式,服务“拦截”了启动它的协程并将其后台操作附加到它上面。但正如我所说,我不确定这是否出于某种原因不被视为反模式。
另外,我知道我的 createChildCoroutInescope()@H_696_11@ 有潜在危险。通过调用它,我们使当前的协程无法完成。这可能是库中不存在此类函数的原因。另一方面,它与做类似的事情并没有真正的不同:
launch {
while (true) {
socket.accept() // assume it is suspendable,not blocking
// launch connection handler
}
}
@H_696_11@@H_801_21@
事实上,从技术角度来看,这两种方法非常相似。它们具有相似的并发结构,但我相信“我的”方法通常更简洁、更强大。
解决方法
我找到了对我的问题的非常好的答案和解释。 Roman Elizarov 在他的一篇文章中准确地讨论了我的问题:https://elizarov.medium.com/coroutIne-context-and-scope-c8b255d59055
他解释说,虽然在技术上可以“捕获”挂起函数的当前上下文并使用它来启动后台协程,但强烈建议不要这样做:
不要这样做!它使启动协程的范围变得不透明和隐式,捕获一些外部 Job@H_696_11@ 以启动新的协程,而无需在函数签名中明确声明它。协程是与您的其余代码并发的一项工作,它的启动必须是显式的。
如果您需要启动一个在您的函数返回后继续运行的协程,那么将您的函数作为 CoroutInescope@H_696_11@ 的扩展或传递 scope: CoroutInescope@H_696_11@ 作为参数以在您的函数签名中明确您的意图。不要让这些功能挂起。
我知道我可以只将 CoroutInescope@H_696_11@/CoroutIneContext@H_696_11@ 传递给函数,但我认为挂起函数是一种更短、更优雅的方法。然而,上述解释使地狱很有意义。如果我们的函数需要获取调用者的协程范围/上下文,请明确说明这一点 - 再简单不过了。
这也与“热”/“冷”执行的概念有关。挂起函数的一大好处是它们允许我们轻松创建长期运行任务的“冷”实现。虽然我认为在协程文档中没有明确指定挂起函数应该是“冷的”,但满足这个要求通常是一个好主意,因为我们的挂起函数的调用者可能会认为它是“冷的”。捕获协程上下文会使我们的函数“热”,因此应该通知调用者这一点。
@H_197_124@
以上是大佬教程为你收集整理的在 Kotlin 中创建子协程范围全部内容,希望文章能够帮你解决在 Kotlin 中创建子协程范围所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。