Kotlin 中的 COROUTINE_SUSPENDED 和 suspendCoroutineOrReturn

4 jvm suspend coroutine kotlin

kotlin 中协程的想法是抽象挂起和回调的概念并编写简单的顺序代码。与线程类似,您永远不需要担心协程是否被挂起。

suspendCoroutineOrReturn它们的目的是什么?COROUTINE_SUSPENDED在什么情况下您会使用它们?

Max*_*xim 5

最近在 1.1 中引入了suspendCoroutineOrReturn和内在函数来解决特定的堆栈溢出问题。COROUTINE_SUSPENDED这是一个例子:

fun problem() = async { 
  repeat(10_000) { 
    await(work()) 
  } 
}
Run Code Online (Sandbox Code Playgroud)

只需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)
  }
}
Run Code Online (Sandbox Code Playgroud)

让我们看一下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
Run Code Online (Sandbox Code Playgroud)

请注意,它接收一个带有延续的块。基本上,它是访问Continuation实例的一种方式,通常是隐藏的,仅在编译期间出现。该块还允许返回任何值 或COROUTINE_SUSPENDED

由于这一切看起来相当复杂,Kotlin 尝试将其隐藏起来并建议仅使用suspendCoroutine函数,该函数在内部为您完成上述所有操作。这是await避免的正确实现StackOverflowError(旁注:await在 Kotlin lib 中提供,它实际上是一个扩展函数,但对于本次讨论来说并不那么重要)

suspend fun <T> await(f: CompletableFuture<T>): T = 
  suspendCoroutine { c -> 
    f.whenComplete { value, exception -> 
      if (exception != null) c.resumeWithException(exception) else
                             c.resume(value)
  }
} 
Run Code Online (Sandbox Code Playgroud)

但是,如果您想接管对协程延续的细粒度控制,则应该在进行外部调用时调用suspendCoroutineOrReturn并返回。COROUTINE_SUSPENDED