CORUTTINE_SUSPENDED和suspendCoroutineOrReturn在Kotlin

kotlin中的协程的概念是抽象暂停和回调的概念,并写出简单的顺序代码。 如果协程暂停或不在线,则无需担心,与线程类似。

suspendCoroutineOrReturnCOROUTINE_SUSPENDED的用途是什么,在什么情况下你会使用它们?

suspendCoroutineOrReturnCOROUTINE_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$stateMachineawaitCompletableFuture.whenCompleteawait$lambdaContinuationImpl.resumeproblem$stateMachineawait ,…

本质上,没有任何事情是暂停的,并且状态机一次又一次地在同一个执行线程中调用自己,结果是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