SLF4j MDC 、トランザクションマネージャ、セキュリティマネージャなど、一部のJVMフレームワークはThreadLocal
を使用してアプリケーションの呼び出しコンテキストを格納します。
ただし、Kotlinコルーチンは異なるスレッドでディスパッチされるため、どのように機能させることができますか?
(質問は GitHubの問題 に触発されています)
コルーチンのThreadLocal
への類似は CoroutineContext
です。
ThreadLocal
- usingライブラリと相互運用するには、フレームワーク固有のスレッドローカルをサポートするカスタム 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...
}
上記の実装は、実行されたスレッドローカルへの変更を追跡し、このコンテキストに保存するため、この方法で、複数の呼び出しがコンテキストを介して「スレッドローカル」データを共有できます。
[〜#〜]更新[〜#〜]:kotlinx.corutines
バージョン0.25.0
以降、Java =コルーチンコンテキストエレメントとしてのThreadLocal
インスタンス。詳細は このドキュメント を参照してください。また、kotlinx-coroutines-slf4j
統合モジュールを介したSLF4J MDCのサポートがすぐに利用できます。