<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Continuation介面是協程中最核心的介面,代表著掛起點之後的續體,程式碼如下:
public interface Continuation<in T> { // 續體的上下文 public val context: CoroutineContext // 該方法用於恢復續體的執行 // result為掛起點執行完成的返回值,T為返回值的型別 public fun resumeWith(result: Result<T>) }
Continuation圖解
ContinuationInterceptor介面繼承自Element介面,是協程中的續體攔截器,程式碼如下:
public interface ContinuationInterceptor : CoroutineContext.Element { // 攔截器的Key companion object Key : CoroutineContext.Key<ContinuationInterceptor> // 攔截器對續體進行攔截時會呼叫該方法,並對continuation進行快取 // 攔截判斷:根據傳入的continuation物件與返回的continuation物件是否相同 public fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> // 當interceptContinuation方法攔截的協程執行完畢後,會呼叫該方法 public fun releaseInterceptedContinuation(continuation: Continuation<*>) { /* do nothing by default */ } // get方法多型實現 public override operator fun <E : CoroutineContext.Element> get(key: CoroutineContext.Key<E>): E? { @OptIn(ExperimentalStdlibApi::class) if (key is AbstractCoroutineContextKey<*, *>) { @Suppress("UNCHECKED_CAST") return if (key.isSubKey(this.key)) key.tryCast(this) as? E else null } @Suppress("UNCHECKED_CAST") return if (ContinuationInterceptor === key) this as E else null } // minusKey方法多型實現 public override fun minusKey(key: CoroutineContext.Key<*>): CoroutineContext { @OptIn(ExperimentalStdlibApi::class) if (key is AbstractCoroutineContextKey<*, *>) { return if (key.isSubKey(this.key) && key.tryCast(this) != null) EmptyCoroutineContext else this } return if (ContinuationInterceptor === key) EmptyCoroutineContext else this } }
CoroutineDispatcher類繼承自AbstractCoroutineContextElement類,實現了ContinuationInterceptor介面,是協程排程器的基礎類別,程式碼如下:
public abstract class CoroutineDispatcher : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { // ContinuationInterceptor的多型實現,排程器本質上就是攔截器 @ExperimentalStdlibApi public companion object Key : AbstractCoroutineContextKey<ContinuationInterceptor, CoroutineDispatcher>( ContinuationInterceptor, { it as? CoroutineDispatcher }) // 用於判斷排程器是否要呼叫dispatch方法進行排程,預設為true public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true // 排程的核心方法,在這裡進行排程,執行block public abstract fun dispatch(context: CoroutineContext, block: Runnable) // 如果排程是由Yield方法觸發的,預設通過dispatch方法實現 @InternalCoroutinesApi public open fun dispatchYield(context: CoroutineContext, block: Runnable): Unit = dispatch(context, block) // ContinuationInterceptor介面的方法,將續體包裹成DispatchedContinuation,並傳入當前排程器 public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = DispatchedContinuation(this, continuation) // 釋放父協程與子協程的關聯。 @InternalCoroutinesApi public override fun releaseInterceptedContinuation(continuation: Continuation<*>) { (continuation as DispatchedContinuation<*>).reusableCancellableContinuation?.detachChild() } // 過載了"+"操作,直接返回others // 因為兩個排程器相加沒有意義,同一個上下文中只能有一個排程器 // 如果需要加的是排程器物件,則直接替換成最新的,因此直接返回 public operator fun plus(other: CoroutineDispatcher): CoroutineDispatcher = other override fun toString(): String = "$classSimpleName@$hexAddress" }
EventLoop類繼承自CoroutineDispatcher類,用於協程中任務的分發執行,只在runBlocking方法中和Dispatchers.Unconfined排程器中使用。與Handler中的Looper類似,在建立後會儲存在當前執行緒的ThreadLocal中。EventLoop本身不支援延時執行任務,如果需要可以自行繼承EventLoop並實現Delay介面,EventLoop中預留了一部分變數和方法用於延時需求的擴充套件。
為什麼協程需要EventLoop呢?協程的本質是續體傳遞,而續體傳遞的本質是回撥,假設在Dispatchers.Unconfined排程下,要連續執行多個suspend方法,就會有多個續體傳遞,假設suspend方法達到一定數量後,就會造成StackOverflow,進而引起崩潰。同樣的,我們知道呼叫runBlocking會阻塞當前執行緒,而runBlocking阻塞的原理就是執行“死迴圈”,因此需要在迴圈中做任務的分發,去執行內部協程在Dispatchers.Unconfined排程器下加入的任務。
EventLoop程式碼如下:
internal abstract class EventLoop : CoroutineDispatcher() { // 用於記錄使用當前EventLoop的runBlocking方法和Dispatchers.Unconfined排程器的數量 private var useCount = 0L // 表示當前的EventLoop是否被暴露給其他的執行緒 // runBlocking會將EventLoop暴露給其他執行緒 // 因此,當runBlocking使用時,shared必須為true private var shared = false // Dispatchers.Unconfined排程器的任務執行佇列 private var unconfinedQueue: ArrayQueue<DispatchedTask<*>>? = null // 處理任務佇列的下一個任務,該方法只能在EventLoop所在的執行緒呼叫 // 返回值<=0,說明立刻執行下一個任務 // 返回值>0,說明等待這段時間後,執行下一個任務 // 返回值為Long.MAX_VALUE,說明佇列裡沒有任務了 public open fun processNextEvent(): Long { if (!processUnconfinedEvent()) return Long.MAX_VALUE return 0 } // 佇列是否為空 protected open val isEmpty: Boolean get() = isUnconfinedQueueEmpty // 下一個任務多長時間後執行 protected open val nextTime: Long get() { val queue = unconfinedQueue ?: return Long.MAX_VALUE return if (queue.isEmpty) Long.MAX_VALUE else 0L } // 任務的核心處理方法 public fun processUnconfinedEvent(): Boolean { // 若佇列為空,則返回 val queue = unconfinedQueue ?: return false // 從隊首取出一個任務,如果為空,則返回 val task = queue.removeFirstOrNull() ?: return false // 執行 task.run() return true } // 表示當前EventLoop是否可以在協程上下文中被呼叫 // EventLoop本質上也是協程上下文 // 如果EventLoop在runBlocking方法中使用,必須返回true public open fun shouldBeProcessedFromContext(): Boolean = false // 向佇列中新增一個任務 public fun dispatchUnconfined(task: DispatchedTask<*>) { // 若佇列為空,則建立一個新的佇列 val queue = unconfinedQueue ?: ArrayQueue<DispatchedTask<*>>().also { unconfinedQueue = it } queue.addLast(task) } // EventLoop當前是否還在被使用 public val isActive: Boolean get() = useCount > 0 // EventLoop當前是否還在被Unconfined排程器使用 public val isUnconfinedLoopActive: Boolean get() = useCount >= delta(unconfined = true) // 判斷佇列是否為空 public val isUnconfinedQueueEmpty: Boolean get() = unconfinedQueue?.isEmpty ?: true // 下面三個方法用於計算使用當前的EventLoop的runBlocking方法和Unconfined排程器的數量 // useCount是一個64位元的數, // 它的高32位元用於記錄Unconfined排程器的數量,低32位元用於記錄runBlocking方法的數量 private fun delta(unconfined: Boolean) = if (unconfined) (1L shl 32) else 1L fun incrementUseCount(unconfined: Boolean = false) { useCount += delta(unconfined) // runBlocking中使用,shared為true if (!unconfined) shared = true } fun decrementUseCount(unconfined: Boolean = false) { useCount -= delta(unconfined) // 如果EventLoop還在被使用 if (useCount > 0) return assert { useCount == 0L } // 如果EventLoop不被使用了,並且在EventLoop中使用過 if (shared) { // 關閉相關資源,並在ThreadLocal中移除 shutdown() } } protected open fun shutdown() {} }
協程中提供了EventLoopImplBase類,間接繼承自EventLoop,實現了Delay介面,用來延時執行任務。同時,協程中還提供單例物件ThreadLocalEventLoop用於EventLoop在ThreadLocal中的儲存。
到此這篇關於Kotlin圖文並茂講解續體與續體攔截器和排程器的文章就介紹到這了,更多相關Kotlin續體內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45