CoroutineContextのお話

KotlinAdventcalendarの24日の記事になります。 今回はcoroutineのCoroutineContext/CoroutineDispacherについて書きたいと思っています。 サンプルコードなどが少なくなってしまったので後ほど詳しいものは上げたいと思っています...

CoroutineContextとは?

現在のJobなどの情報をもっているものです。 またlaunch/asyncを実行する時にCoroutineContextを引数に取ることができます。 こんな感じですね。

launch(coroutineContext){

  //do something…

}

また現在実行しているJobなどをcoroutineを実行する際に渡すことができます。(詳しいJobの説明 ) coroutineContextを使用して新しいcoroutineを生成すると、親子関係になります。 なので、子の中でも親のcontextを使用することができ、親がJobの実行を中断すると子のJobも中断されます。 詳しいことはこちらに載っています。

CoroutineDispatcher - kotlinx-coroutines-core

CoroutineDispacherとは?

coroutineの実行するスレッドを指定することができ、CoroutineContextに含まれているものです。CoroutineDispacherの指定には複数の方法があり、 指定の方法によってdispacherの動作が異なります。

Unconfined

A coroutine dispatcher that is not confined to any specific thread. It executes initial continuation of the coroutine right here in the current call-frame and let the coroutine resume in whatever thread that is used by the corresponding suspending function, without mandating any specific threading policy.

Unconfinedを指定すると、特定のスレッドの指定をしなく、そこに実行されているスレッドで実行することになります。

CommonPool

Represents common pool of shared threads as coroutine dispatcher for compute-intensive tasks. It uses java.util.concurrent.ForkJoinPool when available, which implements efficient work-stealing algorithm for its queues, so every coroutine resumption is dispatched as a separate task even when it already executes inside the pool. When available, it wraps ForkJoinPool.commonPool and provides a similar shared pool where not.


CommonPoolでは名前の通り、共有スレッドであるプール内で処理を行なっています。プール内では可能な時にwork-stealingアルゴリズムが使用されており、それぞれのcoroutineの再開処理などは別タスクとしてdispachされます。 ここでwork-stealing algorithmについて自分がよく知らなかったので調べてみました。 このアルゴリズムを使用しているのが ForkJoinPoolというものです。プール内の全てのスレッドがプール内にあるタスク(他スレッド・プールに送信されたタスク)を見つけて実行する仕組みになっており、サブタスクが多く発生される場合などに効率的な処理が可能になります。

またCommonPoolにCoroutineNameをつけることでPoolを作成する事ができます。

launch(CommonPool + CoroutineName("mycoroutine")){...}

work-stealing algorithmとは?

このアルゴリズムは並行処理などのスケジューリングのアルゴリズムなどに使用されているものです、 このアルゴリズムによって新しい実行プロセスを途中で生成することで、実行時間などを短縮することができます。 このスケジューラーの中では、それぞれのプロセスが連続して実行されるようになっていますが、実行途中で新しいプロセスを生成して並列に実行することもできます。

実行モデルの例

Work stealingは fork-join modelという並行処理に必要なモデルで実行されます。 forkとjoinを繰り返すことで内部で新しいプロセスを生成・実行し、joinすることで結合しています。

function f(a, b):
    c ← fork g(a)
    d ← h(b)
    join
    return c + d

function g(a):
    return a × 2

function h(a):
    b ← fork g(a)
    c ← a + 1
    join
    return b + c

f:id:sasamihoo:20171224165221p:plain

今回はCoroutineStartについても触れられなかったので、実際に実行してどのような違いがあるのかなど年末にまた書ければ良いなと思っています。 それでは良いクリスマスを!!

*引用: Work stealing - Wikipedia