一部のJVMフレームワークでは、SLF4j MDC、トランザクションマネージャ、セキュリティマネージャなどのアプリケーションのコールコンテキストを格納するのに、ThreadLocal
を使用しています。ThreadLocalに依存するコードをKotlinコルーチンで使用する方法
しかし、Kotlinコルーチンは別のスレッドにディスパッチされるため、どのように動作させることができますか?
(質問はGitHub issueに触発された)ThreadLocal
へ
一部のJVMフレームワークでは、SLF4j MDC、トランザクションマネージャ、セキュリティマネージャなどのアプリケーションのコールコンテキストを格納するのに、ThreadLocal
を使用しています。ThreadLocalに依存するコードをKotlinコルーチンで使用する方法
しかし、Kotlinコルーチンは別のスレッドにディスパッチされるため、どのように動作させることができますか?
(質問はGitHub issueに触発された)ThreadLocal
へ
コルーチンのアナログが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...
}
上記実装は、スレッドローカル行ったの変更を追跡し、このコンテキストに格納するので、この方法は、複数の呼び出しは、コンテキストを介して、「スレッドローカル」データを共有することができるであろう。