2017-09-14 1 views
6

一部のJVMフレームワークでは、SLF4j MDC、トランザクションマネージャ、セキュリティマネージャなどのアプリケーションのコールコンテキストを格納するのに、ThreadLocalを使用しています。ThreadLocalに依存するコードをKotlinコルーチンで使用する方法

しかし、Kotlinコルーチンは別のスレッドにディスパッチされるため、どのように動作させることができますか?

(質問はGitHub issueに触発された)ThreadLocal

答えて

8

コルーチンのアナログがCoroutineContextです。

ThreadLocalと相互運用するには、フレームワーク固有のスレッドローカルをサポートするカスタムContinuationInterceptorを実装する必要があります。

ここは例です。私たちはいくつかのアプリケーション固有のデータを格納するために、特定のThreadLocalに依存しているいくつかのフレームワーク(この例ではMyData)を使用することを想定してみましょう:

val myThreadLocal = ThreadLocal<MyData>() 

コルーチンでそれを使用するには、そのコンテキストを実装する必要がありますコルーチンがスレッド上で再開されるたびに、MyDataの現在の値を保持し、対応するThreadLocalに格納します。コードは次のようになります。あなたのコルーチンでそれを使用するには

class MyContext(
    private var myData: MyData, 
    private val dispatcher: ContinuationInterceptor 
) : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { 
    override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = 
     dispatcher.interceptContinuation(Wrapper(continuation)) 

    inner class Wrapper<T>(private val continuation: Continuation<T>): Continuation<T> { 
     private inline fun wrap(block:() -> Unit) { 
      try { 
       myThreadLocal.set(myData) 
       block() 
      } finally { 
       myData = myThreadLocal.get() 
      } 
     } 

     override val context: CoroutineContext get() = continuation.context 
     override fun resume(value: T) = wrap { continuation.resume(value) } 
     override fun resumeWithException(exception: Throwable) = wrap { continuation.resumeWithException(exception) } 
    } 
} 

、あなたがMyContextを使用して、それをあなたのデータの初期値を与えたいディスパッチャを包みます。この値は、コルーチンが再開されたスレッドのスレッドローカルに入れられます。

launch(MyContext(MyData(), CommonPool)) { 
    // do something... 
} 

上記実装は、スレッドローカル行ったの変更を追跡し、このコンテキストに格納するので、この方法は、複数の呼び出しは、コンテキストを介して、「スレッドローカル」データを共有することができるであろう。

関連する問題