协程及Kotlin协程

一、协程是什么?

协程是程序自己控制挂起和恢复的程序。

协程可以实现多任务协作执行。

二、协程作用?

  • 协程可以让异步代码同步化。
  • 协程可以降低异步程序的设计复杂度。

三、协程分类

  • 按调用栈分类:

    • 有栈协程:每个协程都会分配一个单独调用栈,类似于线程的调用栈。
    • 无栈协程:协程不会分配一个单独调用栈,挂起点的状态通过闭包和对象保存。
  • 按调用关系分类:

    • 对称协程:协程调用权可以转移给任意协程,协程之间是对等关系。比如:协程A调度协程B,协程B调度协程C,协程C调度协程A。
    • 非对称协程:调度权只能转移给调度自己的协程,协程存在父子关系。

 

四、Kotlin中协程

Kotlin实现的协程是无栈协程,挂起状态通过Continuation保存,CoroutineContext包含一个协程调度器。

1. 挂起函数及suspend修饰符

通过suspend修饰的函数就是挂起函数。

挂起函数只能通过其它挂起函数或者协程调用。

挂起函数调用时包含了协程的挂起语义。 

Continuation源码:

  1 /**
  2  * Interface representing a continuation after a suspension point that returns a value of type `T`.
  3  */
  4 @SinceKotlin("1.3")
  5 public interface Continuation<in T> {
  6     /**
  7      * The context of the coroutine that corresponds to this continuation.
  8      */
  9     public val context: CoroutineContext
 10 
 11     /**
 12      * Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
 13      * return value of the last suspension point.
 14      */
 15     public fun resumeWith(result: Result<T>)
 16 }
 17 
 18 /**
 19  * Classes and interfaces marked with this annotation are restricted when used as receivers for extension
 20  * `suspend` functions. These `suspend` extensions can only invoke other member or extension `suspend` functions on this particular
 21  * receiver and are restricted from calling arbitrary suspension functions.
 22  */
 23 @SinceKotlin("1.3")
 24 @Target(AnnotationTarget.CLASS)
 25 @Retention(AnnotationRetention.BINARY)
 26 public annotation class RestrictsSuspension
 27 
 28 /**
 29  * Resumes the execution of the corresponding coroutine passing [value] as the return value of the last suspension point.
 30  */
 31 @SinceKotlin("1.3")
 32 @InlineOnly
 33 public inline fun <T> Continuation<T>.resume(value: T): Unit =
 34     resumeWith(Result.success(value))
 35 
 36 /**
 37  * Resumes the execution of the corresponding coroutine so that the [exception] is re-thrown right after the
 38  * last suspension point.
 39  */
 40 @SinceKotlin("1.3")
 41 @InlineOnly
 42 public inline fun <T> Continuation<T>.resumeWithException(exception: Throwable): Unit =
 43     resumeWith(Result.failure(exception))
 44 
 45 
 46 /**
 47  * Creates a [Continuation] instance with the given [context] and implementation of [resumeWith] method.
 48  */
 49 @SinceKotlin("1.3")
 50 @InlineOnly
 51 public inline fun <T> Continuation(
 52     context: CoroutineContext,
 53     crossinline resumeWith: (Result<T>) -> Unit
 54 ): Continuation<T> =
 55     object : Continuation<T> {
 56         override val context: CoroutineContext
 57             get() = context
 58 
 59         override fun resumeWith(result: Result<T>) =
 60             resumeWith(result)
 61     }
 62 
 63 /**
 64  * Creates a coroutine without a receiver and with result type [T].
 65  * This function creates a new, fresh instance of suspendable computation every time it is invoked.
 66  *
 67  * To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
 68  * The [completion] continuation is invoked when the coroutine completes with a result or an exception.
 69  * Subsequent invocation of any resume function on the resulting continuation will produce an [IllegalStateException].
 70  */
 71 @SinceKotlin("1.3")
 72 @Suppress("UNCHECKED_CAST")
 73 public fun <T> (suspend () -> T).createCoroutine(
 74     completion: Continuation<T>
 75 ): Continuation<Unit> =
 76     SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)
 77 
 78 /**
 79  * Creates a coroutine with receiver type [R] and result type [T].
 80  * This function creates a new, fresh instance of suspendable computation every time it is invoked.
 81  *
 82  * To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
 83  * The [completion] continuation is invoked when the coroutine completes with a result or an exception.
 84  * Subsequent invocation of any resume function on the resulting continuation will produce an [IllegalStateException].
 85  */
 86 @SinceKotlin("1.3")
 87 @Suppress("UNCHECKED_CAST")
 88 public fun <R, T> (suspend R.() -> T).createCoroutine(
 89     receiver: R,
 90     completion: Continuation<T>
 91 ): Continuation<Unit> =
 92     SafeContinuation(createCoroutineUnintercepted(receiver, completion).intercepted(), COROUTINE_SUSPENDED)
 93 
 94 /**
 95  * Starts a coroutine without a receiver and with result type [T].
 96  * This function creates and starts a new, fresh instance of suspendable computation every time it is invoked.
 97  * The [completion] continuation is invoked when the coroutine completes with a result or an exception.
 98  */
 99 @SinceKotlin("1.3")
100 @Suppress("UNCHECKED_CAST")
101 public fun <T> (suspend () -> T).startCoroutine(
102     completion: Continuation<T>
103 ) {
104     createCoroutineUnintercepted(completion).intercepted().resume(Unit)
105 }
106 
107 /**
108  * Starts a coroutine with receiver type [R] and result type [T].
109  * This function creates and starts a new, fresh instance of suspendable computation every time it is invoked.
110  * The [completion] continuation is invoked when the coroutine completes with a result or an exception.
111  */
112 @SinceKotlin("1.3")
113 @Suppress("UNCHECKED_CAST")
114 public fun <R, T> (suspend R.() -> T).startCoroutine(
115     receiver: R,
116     completion: Continuation<T>
117 ) {
118     createCoroutineUnintercepted(receiver, completion).intercepted().resume(Unit)
119 }
120 
121 /**
122  * Obtains the current continuation instance inside suspend functions and suspends
123  * the currently running coroutine.
124  *
125  * In this function both [Continuation.resume] and [Continuation.resumeWithException] can be used either synchronously in
126  * the same stack-frame where the suspension function is run or asynchronously later in the same thread or
127  * from a different thread of execution. Subsequent invocation of any resume function will produce an [IllegalStateException].
128  */
129 @SinceKotlin("1.3")
130 @InlineOnly
131 public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T =
132     suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->
133         val safe = SafeContinuation(c.intercepted())
134         block(safe)
135         safe.getOrThrow()
136     }
137 
138 /**
139  * Returns the context of the current coroutine.
140  */
141 @SinceKotlin("1.3")
142 @Suppress("WRONG_MODIFIER_TARGET")
143 @InlineOnly
144 public suspend inline val coroutineContext: CoroutineContext
145     get() {
146         throw NotImplementedError("Implemented as intrinsic")
147     }

 挂起函数格式:

suspend fun test(param: Int): String {
   return "test"
}

挂起函数类型:

suspend (param: Int) -> String

在编译后,解码挂起函数:

suspend fun test(param: Int, continuation: Continuation<String>): String {
    return "test"
}

在函数参数中多了一个Continuation参数,Continuation就是用于保存协程挂起点状态对象。这也是为什么挂起函数只能由其它挂起函数或者协程调用,为什么Kotlin编译器在编译时给挂起函数多加了一个参数。

2. 挂起与未挂起

  • 真正的挂起:就是必须异步调用resume,包括:1. 切换到其它线程的resume;2. 单线程事件循环异步执行;
  • 未挂起:就是未异步调用resume,直接将结果传递出去。

PS:在resume时返回值分两类,1. 函数真正的返回值,比如上面test函数,返回值类型为String类型;2. 返回挂起函数的挂起标识;只有返回挂起标识时才是真正的挂起。

3. 将回调转写成挂起函数:

  • 使用suspendCoroutine获取挂起函数的Continuation;
  • 回调成功分支使用Continuation.resume(value)函数;
  • 回调失败分支使用Continuation.resumeWithException(error)函数;

 4. CoroutineContext 协程上下文包含一个协程调度器。

 

posted @ 2020-06-23 18:14  naray  阅读(3425)  评论(0编辑  收藏  举报