1. 程式人生 > >Kotlin的協程理解

Kotlin的協程理解

  
   
1. 協程,微執行緒,纖程。英文名Coroutine。
2. 協程不是程序也不是執行緒,它的執行過程更類似於子例程。或者說不帶返回值的函式呼叫。
3. 一個程式中可以包含多個協程,類似於 一個程序可以包含多個執行緒。

  協程和執行緒的區別:
  
   多個執行緒相對獨立,有自己的上下文,但他的切換受系統控制
協程也是相對獨立,有自己的上下文,但它的切換是由自己控制的,可以由自身決定是否切換到其它的協程中

  函式呼叫總是一個入口,一次返回,呼叫順序是明確的。
協程看上去也是子程式(函式),但在執行過程中,在子程式內部可中斷,然後轉而執行其他的子程式,在適當的時候再返回來接著執行。

  注意:在一個子程式中中斷,去執行其它子程式,不是函式呼叫,有點類似於CPU的中斷。

  協程的特點在於:一個執行緒執行

  協程的優勢:

  1. 極高的執行效率。協程的子程式切換不是執行緒切換,而是由程式自身控制,因此,協程沒有執行緒切換的開銷。
  2. 不需要多執行緒的鎖機制,因為只有一個執行緒,也不存在 “同時寫變數衝突”,在協程中控制共享資源不加鎖,只需要判斷狀態即可。

接下來我們來看一下Kotlin一些關鍵方法的原始碼

注意:由於Kotlin的協程還在試驗階段,所以一些程式碼或者方法以後很可能會有新的變動,這裡我使用的版本為:
implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-core:0.22.1’
implementation “org.jetbrains.kotlinx:kotlinx-coroutines-android:0.22.1”

  1. 首先是launch方法
/**
 * 該方法將會在不阻塞當前執行緒的情況下啟動新的協程程式,並且將會返回一個 [Job] 物件,該物件是對這個協程程式的引用.
 * 當這個返回的結果 [Job]物件 被 [cancelled] 或者 [Job.cancel]時,這個啟動的協程會被取消.
 * 
 * 可以顯示地為這個新建立的協程指定一個 [context] 上下文物件.
 * 請參閱 "kotlinx.coroutines" 提供的標準上下文實現的 .
 * [CoroutineDispatcher]可使用父協同程式的[context][CoroutineScope.coroutineContext] [scope] [CoroutineScope],
 * 在這種情況下, 該協同程式的返回的[Job] 是其父協同程式 job 的子級。
 * 父作業也可以使用 [parent] 引數顯式指定。
 *
 * 如果上下文沒有任何排程程式或任何其他[ContinuationInterceptor], 則使用 [DefaultDispatcher]。
 *
 * 預設情況下,協程程式會立即計劃執行。
 * 當然,你可以通過指定 `start` 引數選擇不馬上計劃執行。有關詳細資訊, 請參閱 [CoroutineStart] 
 * 
 * 可選 [start] 引數可以設定為 [CoroutineStart] 以_lazily_的方式啟動協同程式 。在這種情況下,
 * 協同程式 [Job] 是在 _new_ 狀態下建立的。可以使用 [start][Job.start] 方法手動地啟動該函式
 * 並且將在第一次呼叫 [join][Job.join] 時隱式啟動。
 * 
 * 預設情況下, 任何捕獲異常會取消父作業(除非明確指定 [CoroutineExceptionHandler]),
 * 這意味著當 "launch" 指定的協程上下文context物件是來自另一個協同程式的上下文context物件時,
 * 任何捕獲異常會導致父協同程式的取消。
 *
 * 請參閱 [newCoroutineContext] 以瞭解對新建立的協同程式可用的除錯設施的說明。
 *
 * @param
context 協程上下文. 預設值為 [DefaultDispatcher]. * @param start 協同程式啟動選項。預設值為 [CoroutineStart. default]。 * @param parent 顯式指定父作業, 會覆蓋 [context] 中的作業 job(如果有的話)。 * @param block 需要在該協同程式中被執行的程式碼塊 */
public actual fun launch( context: CoroutineContext = DefaultDispatcher, start: CoroutineStart = CoroutineStart.DEFAULT, parent: Job? = null, block: suspend CoroutineScope.() -> Unit ): Job { val newContext = newCoroutineContext(context, parent) val coroutine = if (start.isLazy) LazyStandaloneCoroutine(newContext, block) else StandaloneCoroutine(newContext, active = true) coroutine.start(start, coroutine, block) return coroutine }

async方法

  1. 該方法將會建立新的協程, 並將其將來的結果作為 [Deferred] 即延遲的實現返回。
  2. 當該方法返回的結果物件被[cancelled][Job.cancel]後, 當前正在執行的協程被取消。
  3. 可以顯式指定新協程的 [context]即協程上下文物件。
  4. 請參閱 “kotlinx.coroutines” 提供的標準上下文實現的 .
/**
 * Creates new coroutine and returns its future result as an implementation of [Deferred].
 *
 * The running coroutine is cancelled when the resulting object is [cancelled][Job.cancel].
 *
 * The [context] for the new coroutine can be explicitly specified.
 * See [CoroutineDispatcher] for the standard context implementations that are provided by `kotlinx.coroutines`.
 * The [context][CoroutineScope.coroutineContext] of the parent coroutine from its [scope][CoroutineScope] may be used,
 * in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
 * The parent job may be also explicitly specified using [parent] parameter.
 *
 * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [DefaultDispatcher] is used.
 *
 * By default, the coroutine is immediately scheduled for execution.
 * Other options can be specified via `start` parameter. See [CoroutineStart] for details.
 * An optional [start] parameter can be set to [CoroutineStart.LAZY] to start coroutine _lazily_. In this case,,
 * the resulting [Deferred] is created in _new_ state. It can be explicitly started with [start][Job.start]
 * function and will be started implicitly on the first invocation of [join][Job.join] or [await][Deferred.await].
 *
 * @param context context of the coroutine. The default value is [DefaultDispatcher].
 * @param start coroutine start option. The default value is [CoroutineStart.DEFAULT].
 * @param parent explicitly specifies the parent job, overrides job from the [context] (if any).*
 * @param block the coroutine code.
 */
public actual fun <T> async(
    context: CoroutineContext = DefaultDispatcher,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    parent: Job? = null,
    block: suspend CoroutineScope.() -> T
): Deferred<T> {
    val newContext = newCoroutineContext(context, parent)
    val coroutine = if (start.isLazy)
        LazyDeferredCoroutine(newContext, block) else
        DeferredCoroutine<T>(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

需要了解的一些內容

  1. DefaultDispatcher :[launch]和[async]等標準協程生成器 預設使用的CoroutineDispatcher協程排程者,負責任務的排程,它當前的值等於 CommonPool, 但該值將來會發生變化;

  2. CommonPool :表示共享執行緒的公用池,作為計算密集型任務的協程排程器。當可用時, 它使用 [java.util.concurrent.ForkJoinPool], 它為其佇列實現了高效的工作竊取演算法, 因此, 即使在池中已經執行了每個協程恢復, 也會作為單獨的任務進行排程。
    如果可用, 它將包裝 “ForkJoinPool commonPool”, 並提供一個類似的共享池。