我的问题是理论上的。 我对kotlin还是很陌生(仅通过了本教程,没有编写任何实际代码)。
通读语言参考资料后,我发现自己对“暂停”是关键字这一事实感到困惑,但是在关键字列表中找不到“启动”之类的东西。这使我认为存在一些不对称性-“挂起”是编译器功能,而“启动”是库功能。我的理解正确吗?如果是这样,将两者都实现为库功能还是同时将其实现为编译器功能会更好吗?
我一直认为您总是可以使用可用的语言功能编写自己的标准库,但是我仍然看不到这是否真的适用于这种情况。
TL; DR::我是否可以使用纯Kotlin启动协程,而不导入任何库(无论多么丑陋)?
答案 0 :(得分:3)
我可以使用纯Kotlin启动协程吗,而无需导入任何库(无论多么丑陋)?
不。所有协程生成器都在kotlinx.coroutines
库中,因此至少需要这样做。现在,从理论上讲,您可以自己重新实现此功能。但是可能你不应该。
对于StackOverflow答案,如何完成此操作太长了,但是请尝试从Java调用此Kotlin类的方法:
class AsyncWorks {
suspend fun print() {
println("Hello")
}
}
您将看到,尽管Kotlin方法没有参数,但在Java中它需要Continuation<? super Unit>
。这就是suspend
关键字的作用。它将Continuation<T>
添加为函数的最后一个参数。
既可以同时实现为库功能,也可以更好 都作为编译器功能吗?
理想情况下,您希望所有内容都成为“库功能”,因为它更容易发展。从语言中删除关键字非常困难。从理论上讲,可以避免使用suspend
作为关键字。 Quasar是框架,而是使用注释。另一方面,Go编程语言假定所有功能都是可挂起的。所有这些方法都有其优点和缺点。
Kotlin决定务实,并添加suspend
关键字,由开发人员自行决定。如果您对此主题感兴趣,我强烈推荐Kotlin协程的作者Roman Elizarov的演讲,它解释了他们的决定:https://www.youtube.com/watch?v=Mj5P47F6nJg
答案 1 :(得分:2)
suspend
标记向函数签名添加了一个隐藏的连续参数,并完全更改了实现字节码。悬停点不会归结为辅助函数调用,而是将您的线性程序代码转换为状态机,该状态保留在连续对象中。产生的字节码甚至无法表示为Java程序代码。
相反,launch
只是基于暂停/恢复原语的常规库代码。
答案 2 :(得分:1)
因为协程对于don't support launch的用例有效。因为suspend
需要一些specific support from the compiler和launch
,如果您已经有了suspend
,则不需要。因为结构化并发是语言功能之上的library framework,而launch
是该特定框架的一部分,所以它会根据语言的需求做出特定选择。
startCoroutine
可以启动不带任何库的协程。 kotlin.coroutines
是Kotlin的一部分,而不是图书馆。
答案 3 :(得分:1)
在这里回答我自己的问题。
在科特林呆了一年之后,我倾向于认为这确实是可能的。
suspend
语言功能会创建一个额外的类,并在每次调用您的暂停函数时实例化它。此类扩展了ContinuationImpl
并存储了协程的进度-到目前为止,它可以执行。
因此,需要编写一个自定义的调度程序,该调度程序将能够管理连续对象的队列以确定现在必须运行哪个对象,以及一个launch
函数,它将采用新创建的连续对象并传递交给调度员。
现在,这仍然是不对称的-ContinuationImpl
位于kotlin.coroutines.jvm.internal
中,因此编译器假定此程序包存在。如果真的要完全删除标准库,他将需要实现该程序包才能使用suspend
关键字。
我不是科特琳专家,所以我可能错了。
答案 4 :(得分:0)
@Alexey Soshin的说法不太正确。
您可以在不使用库的情况下使用协程,这很容易。这是有关最简单的暂停协程示例的示例,该示例对协程库的依赖性为0。
import kotlin.coroutines.*
fun main() {
lateinit var context: Continuation<Unit>
suspend {
val extra="extra"
println("before suspend $extra")
suspendCoroutine<Unit> { context = it }
println("after suspend $extra")
}.startCoroutine(
object : Continuation<Unit> {
override val context: CoroutineContext = EmptyCoroutineContext
// called when a coroutine ends. do nothing.
override fun resumeWith(result: Result<Unit>) {
result.onFailure { ex : Throwable -> throw ex }
}
}
)
println("kick it")
context.resume(Unit)
}
在play.kotlinlang.org网站上运行正常。
从此代码中可以看到,任何用suspend
装饰的lambda上都带有startCourtine()
。
实际上,我认为标准集合类中的SequenceBuilder()
使用像这样的简单协程来生成序列,而不依赖于协程库。
编译器正在对协程进行繁重的工作,在每个可能的挂起点将代码分成不同的“方法”。查看此代码的Java代码,您将看到它被“拆分”为switch语句。一种情况是暂停之前,另一种情况是之后。
该库为您提供了很多不错的东西.....而且您几乎总是会使用它(请问为什么不呢?),但实际上并不需要它。