CORUTTINE_SUSPENDED和suspendCoroutineOrReturn在Kotlin
kotlin中的协程的概念是抽象暂停和回调的概念,并写出简单的顺序代码。 如果协程暂停或不在线,则无需担心,与线程类似。
suspendCoroutineOrReturn
和COROUTINE_SUSPENDED
的用途是什么,在什么情况下你会使用它们?
suspendCoroutineOrReturn
和COROUTINE_SUSPENDED
内在函数最近在1.1中引入,以解决特定的堆栈溢出问题。 这里是一个例子:
fun problem() = async { repeat(10_000) { await(work()) } }
await
等待完成的地方:
suspend fun <T> await(f: CompletableFuture<T>, c: Continuation<T>): Unit { f.whenComplete { value, exception -> // <- await$lambda if (exception != null) c.resumeWithException(exception) else c.resume(value) } }
让我们看看work
没有真正挂起的情况,但立即返回结果(例如,缓存)。 在Kotlin中编译进入协程的状态机将进行以下调用: problem$stateMachine
, await
, CompletableFuture.whenComplete
, await$lambda
, ContinuationImpl.resume
, problem$stateMachine
, await
,…
本质上,没有任何事情是暂停的,并且状态机一次又一次地在同一个执行线程中调用自己,结果是StackOverflowError
。
建议的解决方案是允许await
返回一个特殊的令牌( COROUTINE_SUSPENDED
)来区分协程实际上是否挂起,这样状态机可以避免堆栈溢出。 接下来, suspendCoroutineOrReturn
用来控制协程的执行。 这是它的声明:
public inline suspend fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?): T
请注意,它会收到一个提供了延续的块。 基本上这是一种访问Continuation
实例的方法,它通常隐藏起来,只在编译期间出现。 该块也被允许返回任何值或COROUTINE_SUSPENDED
。
由于这一切看起来相当复杂,Kotlin试图隐藏它,并建议只使用suspendCoroutine
函数,它在内部为您提供上面提到的所有东西。 这是避免StackOverflowError
的正确的await
实现(注意: await
在Kotlin库中提供,实际上它是一个扩展函数,但对于这个讨论并不重要)
suspend fun <T> await(f: CompletableFuture<T>): T = suspendCoroutine { c -> f.whenComplete { value, exception -> if (exception != null) c.resumeWithException(exception) else c.resume(value) } }
但是,如果您想要接管对协程继续的精细控制,则应在每次进行外部调用时调用suspendCoroutineOrReturn
并返回COROUTINE_SUSPENDED
。